nomadcode/services/core/internal/gitoevents/client_test.go
toki 2afa534338 feat: gito events client 및 gito sync bridge 구현, config 확장
- gitoevents: Gito 이벤트 클라이언트 및 이벤트 모델 구현
- gitosync: 브리지, 러너, 스캐너 구현으로 브랜치 이벤트 및 작업 항목 동기화
- config: 새 설정 항목 추가 및 테스트
- roadmapsync: plane projection 수정 및 테스트
- agent-task: G07 관련 계획 및 코드 리뷰 로그 추가
- roadmap: PHASE 및 마일스톤 업데이트
- contracts: Flutter core API 후보 노트 업데이트
2026-06-14 15:31:25 +09:00

181 lines
4.3 KiB
Go

package gitoevents
import (
"context"
"errors"
"io"
"sync"
"testing"
"github.com/nomadcode/nomadcode-core/internal/protosocket"
)
// fakeTransport is a scripted Transport for driving Client in tests.
type fakeTransport struct {
mu sync.Mutex
connected bool
closed bool
written []protosocket.Envelope
inbound []protosocket.Envelope
readIdx int
}
func (f *fakeTransport) Connect(context.Context) error {
f.mu.Lock()
defer f.mu.Unlock()
f.connected = true
return nil
}
func (f *fakeTransport) Write(_ context.Context, env protosocket.Envelope) error {
f.mu.Lock()
defer f.mu.Unlock()
f.written = append(f.written, env)
return nil
}
func (f *fakeTransport) Read(context.Context) (protosocket.Envelope, error) {
f.mu.Lock()
defer f.mu.Unlock()
if f.readIdx >= len(f.inbound) {
return protosocket.Envelope{}, io.EOF
}
env := f.inbound[f.readIdx]
f.readIdx++
return env, nil
}
func (f *fakeTransport) Close() error {
f.mu.Lock()
defer f.mu.Unlock()
f.closed = true
return nil
}
func TestClientSendsSubscribeEnvelope(t *testing.T) {
ft := &fakeTransport{}
client, err := NewClient(ft, Options{RepoID: "nomadcode", Branch: "develop"})
if err != nil {
t.Fatalf("NewClient: %v", err)
}
if err := client.Run(context.Background()); !errors.Is(err, io.EOF) {
t.Fatalf("Run: got %v, want io.EOF", err)
}
if !ft.connected {
t.Fatal("expected transport to be connected")
}
if !ft.closed {
t.Fatal("expected transport to be closed")
}
if len(ft.written) != 1 {
t.Fatalf("written envelopes: got %d, want 1", len(ft.written))
}
sub := ft.written[0]
if sub.Channel != EventChannel || sub.Action != SubscribeAction {
t.Fatalf("subscribe envelope: got %q/%q", sub.Channel, sub.Action)
}
if sub.Payload["repo_id"] != "nomadcode" || sub.Payload["branch"] != "develop" {
t.Fatalf("subscribe payload: got %v", sub.Payload)
}
}
func TestClientForwardsTargetEventToHandler(t *testing.T) {
ft := &fakeTransport{
inbound: []protosocket.Envelope{branchUpdatedEnvelope()},
}
var got []BranchUpdatedEvent
client, err := NewClient(ft, Options{
RepoID: "nomadcode",
Branch: "develop",
Handler: func(_ context.Context, ev BranchUpdatedEvent) {
got = append(got, ev)
},
})
if err != nil {
t.Fatalf("NewClient: %v", err)
}
if err := client.Run(context.Background()); !errors.Is(err, io.EOF) {
t.Fatalf("Run: got %v, want io.EOF", err)
}
if len(got) != 1 {
t.Fatalf("handler events: got %d, want 1", len(got))
}
if got[0].RepoID != "nomadcode" || got[0].After != "new-sha" {
t.Fatalf("forwarded event: got %+v", got[0])
}
}
func TestClientDropsOffTargetAndMalformedEvents(t *testing.T) {
offRepo := branchUpdatedEnvelope()
offRepo.Payload["repo_id"] = "other-repo"
offBranch := branchUpdatedEnvelope()
offBranch.Payload["branch"] = "main"
wrongAction := branchUpdatedEnvelope()
wrongAction.Action = "task.status.changed"
malformed := branchUpdatedEnvelope()
malformed.Payload["changed_files"] = "not-a-list"
onTarget := branchUpdatedEnvelope()
ft := &fakeTransport{
inbound: []protosocket.Envelope{offRepo, offBranch, wrongAction, malformed, onTarget},
}
var got []BranchUpdatedEvent
client, err := NewClient(ft, Options{
RepoID: "nomadcode",
Branch: "develop",
Handler: func(_ context.Context, ev BranchUpdatedEvent) {
got = append(got, ev)
},
})
if err != nil {
t.Fatalf("NewClient: %v", err)
}
if err := client.Run(context.Background()); !errors.Is(err, io.EOF) {
t.Fatalf("Run: got %v, want io.EOF", err)
}
if len(got) != 1 {
t.Fatalf("handler events: got %d, want only the on-target event", len(got))
}
}
func TestClientStopsOnContextCancel(t *testing.T) {
ft := &fakeTransport{
inbound: []protosocket.Envelope{branchUpdatedEnvelope()},
}
ctx, cancel := context.WithCancel(context.Background())
cancel()
client, err := NewClient(ft, Options{RepoID: "nomadcode", Branch: "develop"})
if err != nil {
t.Fatalf("NewClient: %v", err)
}
if err := client.Run(ctx); err != nil {
t.Fatalf("Run with cancelled context: got %v, want nil", err)
}
}
func TestNewClientRequiresRepoID(t *testing.T) {
if _, err := NewClient(&fakeTransport{}, Options{}); err == nil {
t.Fatal("expected error when repo_id is empty")
}
}
func TestNewClientRequiresTransport(t *testing.T) {
if _, err := NewClient(nil, Options{RepoID: "nomadcode"}); err == nil {
t.Fatal("expected error when transport is nil")
}
}