package payments import ( "slices" "testing" ) // allPresent is an account attached to every source (the maximal present set). var allPresent = []Source{SourceDirect, SourceVK, SourceTelegram} func TestSpendableSources(t *testing.T) { tests := []struct { name string ctx Context present []Source want []Source }{ {"vk android, vk present", Context{Kind: SourceVK, Subtype: "android"}, allPresent, []Source{SourceVK}}, // VK-iOS spends its own vk segment: the freeze is purchase-only, spending is allowed. {"vk ios spends vk", Context{Kind: SourceVK, Subtype: SubtypeIOS}, allPresent, []Source{SourceVK}}, {"vk android, vk absent", Context{Kind: SourceVK, Subtype: "android"}, []Source{SourceDirect}, nil}, {"telegram", Context{Kind: SourceTelegram, Subtype: "web"}, allPresent, []Source{SourceTelegram}}, {"telegram, tg absent", Context{Kind: SourceTelegram}, []Source{SourceVK}, nil}, {"direct all present, priority", Context{Kind: SourceDirect, Subtype: "web"}, allPresent, []Source{SourceDirect, SourceVK, SourceTelegram}}, {"direct, only vk+tg attached", Context{Kind: SourceDirect}, []Source{SourceTelegram, SourceVK}, []Source{SourceVK, SourceTelegram}}, {"direct, nothing attached", Context{Kind: SourceDirect}, nil, nil}, {"untrusted fail-closed", Context{}, allPresent, nil}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if got := spendableSources(tc.ctx, tc.present); !slices.Equal(got, tc.want) { t.Errorf("spendableSources(%+v, %v) = %v, want %v", tc.ctx, tc.present, got, tc.want) } }) } } func TestApplicableOrigins(t *testing.T) { tests := []struct { name string ctx Context present []Source want []Source }{ // A vk-origin benefit applies on VK-iOS (spending is allowed there — the freeze is purchase-only). {"vk ios applies", Context{Kind: SourceVK, Subtype: SubtypeIOS}, allPresent, []Source{SourceVK}}, {"vk android", Context{Kind: SourceVK, Subtype: "android"}, allPresent, []Source{SourceVK}}, {"telegram", Context{Kind: SourceTelegram}, allPresent, []Source{SourceTelegram}}, {"direct all, priority", Context{Kind: SourceDirect}, allPresent, []Source{SourceDirect, SourceVK, SourceTelegram}}, {"untrusted fail-closed", Context{}, allPresent, nil}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if got := applicableOrigins(tc.ctx, tc.present); !slices.Equal(got, tc.want) { t.Errorf("applicableOrigins(%+v, %v) = %v, want %v", tc.ctx, tc.present, got, tc.want) } }) } } // TestComplianceWall is the named unit-level compliance regression: a direct origin (externally // paid, outside any store cash desk) must NEVER be applicable inside a VK or TG wrapper, and no // segment is ever spendable there beyond the same-named one. The dangerous direction stays shut. func TestComplianceWall(t *testing.T) { for _, kind := range []Source{SourceVK, SourceTelegram} { for _, sub := range []string{"android", "ios", "web"} { ctx := Context{Kind: kind, Subtype: sub} if slices.Contains(applicableOrigins(ctx, allPresent), SourceDirect) { t.Errorf("direct origin applies inside %s/%s — compliance wall breached", kind, sub) } for _, s := range spendableSources(ctx, allPresent) { if s != kind { t.Errorf("foreign segment %s spendable inside %s/%s", s, kind, sub) } } // The opposite store's origin must not leak in either (vk⊥tg). other := SourceVK if kind == SourceVK { other = SourceTelegram } if slices.Contains(applicableOrigins(ctx, allPresent), other) { t.Errorf("%s origin applies inside %s — cross-store leak", other, kind) } } } } func TestVisibleSources(t *testing.T) { if got := visibleSources(Context{Kind: SourceVK, Subtype: SubtypeIOS}); !slices.Equal(got, []Source{SourceVK}) { t.Errorf("VK visible = %v, want [vk] (direct/tg hidden in a store)", got) } if got := visibleSources(Context{Kind: SourceDirect}); !slices.Equal(got, allPresent) { t.Errorf("direct visible = %v, want all three", got) } if got := visibleSources(Context{}); !slices.Equal(got, allPresent) { t.Errorf("untrusted visible = %v, want all three (view-only)", got) } } func TestSourceForIdentityKind(t *testing.T) { tests := []struct { kind string want Source ok bool }{ {"vk", SourceVK, true}, {"telegram", SourceTelegram, true}, {"email", SourceDirect, true}, {"robot", "", false}, {"unknown", "", false}, } for _, tc := range tests { got, ok := SourceForIdentityKind(tc.kind) if got != tc.want || ok != tc.ok { t.Errorf("SourceForIdentityKind(%q) = %q,%v want %q,%v", tc.kind, got, ok, tc.want, tc.ok) } } } func TestNewContextTrusted(t *testing.T) { if c := NewContext("vk", "ios"); !c.Trusted() || !c.vkFrozen() { t.Errorf("vk/ios: trusted=%v frozen=%v, want true/true", c.Trusted(), c.vkFrozen()) } if c := NewContext("bogus", "web"); c.Trusted() { t.Errorf("bogus kind should be untrusted") } if c := NewContext("", ""); c.Trusted() { t.Errorf("empty kind should be untrusted") } if c := NewContext("direct", "web"); c.vkFrozen() { t.Errorf("direct is never frozen") } }