package main import ( "encoding/json" "net/http/httptest" "strings" "testing" ) // fixed wallet vector derived in the browser from the mnemonic // "legal winner thank year wave sausage worth useful legal winner thank yellow" // using the unibus-sign-v1 / unibus-kex-v1 HKDF scheme. Used to assert the Go // side accepts the browser-derived key sizes. const ( fixSignPub = "3d594317212e53a3685b305539f6789eb8c538579e350ca795278b180ebb53db" fixSignPriv = "94485d66ac958e23546be2e3b7575a47e1264bdf082e09abb7ad02ab32fcd55e3d594317212e53a3685b305539f6789eb8c538579e350ca795278b180ebb53db" fixKexPub = "f3561ca116e4444b8880b8c0a35f2c9e85804d8628006facd84b1a6146208257" fixKexPriv = "f6ffdf15e5ee2af0494897ff43e61a06d632af425a0372cb53a7c3e0f84c2bb2" ) func TestIdentityFromHex(t *testing.T) { id, err := identityFromHex(fixSignPub, fixSignPriv, fixKexPub, fixKexPriv) if err != nil { t.Fatalf("identityFromHex valid vector: %v", err) } if len(id.SignPub) != 32 || len(id.SignPriv) != 64 || len(id.KexPub) != 32 || len(id.KexPriv) != 32 { t.Fatalf("wrong sizes: %d/%d/%d/%d", len(id.SignPub), len(id.SignPriv), len(id.KexPub), len(id.KexPriv)) } // Wrong sign_priv size (32 instead of 64) must be rejected. if _, err := identityFromHex(fixSignPub, fixSignPub, fixKexPub, fixKexPriv); err == nil { t.Fatalf("expected error for short sign_priv") } // Non-hex must be rejected. if _, err := identityFromHex("zz", fixSignPriv, fixKexPub, fixKexPriv); err == nil { t.Fatalf("expected error for non-hex sign_pub") } } func TestValidHexKey(t *testing.T) { if !validHexKey(fixSignPub) { t.Fatalf("fixSignPub should be a valid 32-byte hex key") } if validHexKey("abcd") { t.Fatalf("short key should be invalid") } if validHexKey(strings.Repeat("z", 64)) { t.Fatalf("non-hex key should be invalid") } } func TestNewRegistrarParsesMockTokens(t *testing.T) { r := newRegistrar("", "demo=demo:member, bob=bob, alice=alice:admin") if len(r.mockTokens) != 3 { t.Fatalf("want 3 mock tokens, got %d", len(r.mockTokens)) } if r.mockTokens["demo"].role != "member" || r.mockTokens["demo"].handle != "demo" { t.Fatalf("demo token parsed wrong: %+v", r.mockTokens["demo"]) } if r.mockTokens["bob"].role != "member" { t.Fatalf("bob should default to role member, got %q", r.mockTokens["bob"].role) } if r.mockTokens["alice"].role != "admin" { t.Fatalf("alice should be admin, got %q", r.mockTokens["alice"].role) } } // post builds a server with only a registrar (the register path does not touch a // gateway) and runs one POST /api/register, returning status + decoded body. func postRegister(t *testing.T, s *server, body string) (int, map[string]string) { t.Helper() req := httptest.NewRequest("POST", "/api/register", strings.NewReader(body)) w := httptest.NewRecorder() s.handleRegister(w, req) var m map[string]string _ = json.Unmarshal(w.Body.Bytes(), &m) return w.Code, m } func TestHandleRegisterMockSingleUse(t *testing.T) { s := &server{registrar: newRegistrar("", "demo=demo:member")} // 1) valid token + valid keys => 201 with the invite's handle/role. code, body := postRegister(t, s, `{"token":"demo","sign_pub":"`+fixSignPub+`","kex_pub":"`+fixKexPub+`"}`) if code != 201 { t.Fatalf("first register: want 201, got %d (%v)", code, body) } if body["handle"] != "demo" || body["role"] != "member" { t.Fatalf("first register body: %v", body) } // 2) same token again => 409 (single-use consumed). code, _ = postRegister(t, s, `{"token":"demo","sign_pub":"`+fixSignPub+`","kex_pub":"`+fixKexPub+`"}`) if code != 409 { t.Fatalf("reused token: want 409, got %d", code) } } func TestHandleRegisterValidation(t *testing.T) { s := &server{registrar: newRegistrar("", "demo=demo:member")} // bad sign_pub (too short) => 400 if code, _ := postRegister(t, s, `{"token":"demo","sign_pub":"abcd","kex_pub":"`+fixKexPub+`"}`); code != 400 { t.Fatalf("short sign_pub: want 400, got %d", code) } // missing token => 400 if code, _ := postRegister(t, s, `{"sign_pub":"`+fixSignPub+`","kex_pub":"`+fixKexPub+`"}`); code != 400 { t.Fatalf("missing token: want 400, got %d", code) } // unknown token with no mock match and no register-url => 400 if code, _ := postRegister(t, s, `{"token":"nope","sign_pub":"`+fixSignPub+`","kex_pub":"`+fixKexPub+`"}`); code != 400 { t.Fatalf("unknown token: want 400, got %d", code) } }