package transcode_test import ( "context" "encoding/json" "net/http" "testing" flatbuffers "github.com/google/flatbuffers/go" "scrabble/gateway/internal/transcode" "scrabble/gateway/internal/vkid" fb "scrabble/pkg/fbs/scrabblefb" ) func linkVKPayload(code, deviceID, verifier string) []byte { b := flatbuffers.NewBuilder(64) c := b.CreateString(code) d := b.CreateString(deviceID) v := b.CreateString(verifier) fb.LinkVKRequestStart(b) fb.LinkVKRequestAddCode(b, c) fb.LinkVKRequestAddDeviceId(b, d) fb.LinkVKRequestAddCodeVerifier(b, v) b.Finish(fb.LinkVKRequestEnd(b)) return b.FinishedBytes() } // fakeVKIDExchanger records the exchange inputs and returns a canned identity. type fakeVKIDExchanger struct { id vkid.Identity err error gotCode, gotDevice, gotVerifier string } func (f *fakeVKIDExchanger) Exchange(_ context.Context, code, deviceID, codeVerifier string) (vkid.Identity, error) { f.gotCode, f.gotDevice, f.gotVerifier = code, deviceID, codeVerifier return f.id, f.err } func TestLinkVKExchangesAndForwards(t *testing.T) { var gotExternalID string backend, cleanup := fakeBackend(t, func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/v1/user/link/vk" { t.Errorf("path = %q", r.URL.Path) } var body struct { ExternalID string `json:"external_id"` } _ = json.NewDecoder(r.Body).Decode(&body) gotExternalID = body.ExternalID _, _ = w.Write([]byte(`{"status":"linked"}`)) }) defer cleanup() ex := &fakeVKIDExchanger{id: vkid.Identity{ExternalID: "777"}} reg := transcode.NewRegistry(backend, nil, transcode.WithVKLink(ex)) op, ok := reg.Lookup(transcode.MsgLinkVK) if !ok { t.Fatal("link.vk.confirm not registered") } payload, err := op.Handler(context.Background(), transcode.Request{UserID: "u-1", Payload: linkVKPayload("the-code", "dev-1", "verifier-1")}) if err != nil { t.Fatalf("handler: %v", err) } // The PKCE inputs from the wire must reach the exchanger verbatim... if ex.gotCode != "the-code" || ex.gotDevice != "dev-1" || ex.gotVerifier != "verifier-1" { t.Errorf("exchange got %q/%q/%q", ex.gotCode, ex.gotDevice, ex.gotVerifier) } // ...and the resolved vk id must be the one forwarded to the backend. if gotExternalID != "777" { t.Errorf("backend external_id = %q, want 777", gotExternalID) } if string(fb.GetRootAsLinkResult(payload, 0).Status()) != "linked" { t.Error("expected a linked result") } } func TestLinkVKUnregisteredWithoutExchanger(t *testing.T) { backend, cleanup := fakeBackend(t, func(http.ResponseWriter, *http.Request) {}) defer cleanup() reg := transcode.NewRegistry(backend, nil) if _, ok := reg.Lookup(transcode.MsgLinkVK); ok { t.Error("link.vk.confirm must be unregistered when no VK ID exchanger is configured") } if _, ok := reg.Lookup(transcode.MsgLinkVKMerge); ok { t.Error("link.vk.merge must be unregistered when no VK ID exchanger is configured") } }