test(handlers): add _Pure test suite for org_import.go pure function edge cases #1379

Open
core-be wants to merge 1 commits from test/org-import-pure-funcs into main
@@ -0,0 +1,458 @@
package handlers
import (
"sort"
"testing"
)
// ─────────────────────────────────────────────────────────────────────────────
// envRequirementKey tests
// ─────────────────────────────────────────────────────────────────────────────
func TestEnvRequirementKey_SingleMember_Pure(t *testing.T) {
key := envRequirementKey([]string{"API_KEY"})
if key != "API_KEY" {
t.Errorf("single member: got %q, want %q", key, "API_KEY")
}
}
func TestEnvRequirementKey_TwoMembersSorted_Pure(t *testing.T) {
// envRequirementKey sorts before joining, so [B, A] and [A, B] produce same key.
keyBA := envRequirementKey([]string{"B", "A"})
keyAB := envRequirementKey([]string{"A", "B"})
if keyBA != keyAB {
t.Errorf("sort invariance: got %q vs %q", keyBA, keyAB)
}
if keyBA != "A\x00B" {
t.Errorf("sort result: got %q, want %q", keyBA, "A\x00B")
}
}
func TestEnvRequirementKey_ThreeMembers_Pure(t *testing.T) {
key := envRequirementKey([]string{"C", "A", "B"})
if key != "A\x00B\x00C" {
t.Errorf("three members sorted: got %q, want %q", key, "A\x00B\x00C")
}
}
func TestEnvRequirementKey_Empty_Pure(t *testing.T) {
key := envRequirementKey([]string{})
if key != "" {
t.Errorf("empty: got %q, want %q", key, "")
}
}
func TestEnvRequirementKey_DedupBySort_Pure(t *testing.T) {
// Two lists that are permutations of each other must have identical keys.
key1 := envRequirementKey([]string{"Z", "M", "A"})
key2 := envRequirementKey([]string{"A", "Z", "M"})
if key1 != key2 {
t.Errorf("permutation invariance: got %q vs %q", key1, key2)
}
}
// ─────────────────────────────────────────────────────────────────────────────
// sanitizeEnvMembers tests
// ─────────────────────────────────────────────────────────────────────────────
func TestSanitizeEnvMembers_AllValid_Pure(t *testing.T) {
members := []string{"API_KEY", "ANTHROPIC_API_KEY", "SECRET_123"}
got, ok := sanitizeEnvMembers(members, "test")
if !ok {
t.Error("expected ok=true for all valid names")
}
if len(got) != 3 {
t.Errorf("length: got %d, want 3", len(got))
}
}
func TestSanitizeEnvMembers_FiltersLowercase_Pure(t *testing.T) {
members := []string{"api_key", "ANTHROPIC_API_KEY", "VALID_NAME"}
got, ok := sanitizeEnvMembers(members, "test")
if !ok {
t.Error("expected ok=true for mixed case")
}
if len(got) != 2 {
t.Errorf("length: got %d, want 2", len(got))
}
want := []string{"ANTHROPIC_API_KEY", "VALID_NAME"}
for i, w := range want {
if got[i] != w {
t.Errorf("got[%d]=%q, want %q", i, got[i], w)
}
}
}
func TestSanitizeEnvMembers_FiltersInvalidCharacters_Pure(t *testing.T) {
members := []string{"VALID", "in valid", "ALSO-INVALID", "SPACE KEY", "hyphen-key", ""}
got, ok := sanitizeEnvMembers(members, "test")
if !ok {
t.Error("expected ok=true (VALID was present)")
}
if len(got) != 1 || got[0] != "VALID" {
t.Errorf("got %v, want [VALID]", got)
}
}
func TestSanitizeEnvMembers_AllInvalid_Pure(t *testing.T) {
members := []string{"lowercase", "has-dash", "has space"}
got, ok := sanitizeEnvMembers(members, "test")
if ok {
t.Error("expected ok=false when all members invalid")
}
if len(got) != 0 {
t.Errorf("got %v, want []", got)
}
}
func TestSanitizeEnvMembers_EmptyInput_Pure(t *testing.T) {
members := []string{}
got, ok := sanitizeEnvMembers(members, "test")
if ok {
t.Error("expected ok=false for empty input")
}
if len(got) != 0 {
t.Errorf("got %v, want []", got)
}
}
func TestSanitizeEnvMembers_EmptyStringNotLogged_Pure(t *testing.T) {
// Empty string is filtered silently (not logged) — verify no panic.
members := []string{"VALID", ""}
got, ok := sanitizeEnvMembers(members, "test")
if !ok || len(got) != 1 || got[0] != "VALID" {
t.Errorf("unexpected result: ok=%v got=%v", ok, got)
}
}
func TestSanitizeEnvMembers_StartsWithDigit_Pure(t *testing.T) {
members := []string{"123KEY", "VALID_KEY"}
got, ok := sanitizeEnvMembers(members, "test")
if !ok {
t.Error("expected ok=true (VALID_KEY was present)")
}
if len(got) != 1 || got[0] != "VALID_KEY" {
t.Errorf("got %v, want [VALID_KEY]", got)
}
}
// ─────────────────────────────────────────────────────────────────────────────
// flattenAndSortRequirements tests
// ─────────────────────────────────────────────────────────────────────────────
func TestFlattenAndSortRequirements_SinglesFirst_Pure(t *testing.T) {
by := map[string]EnvRequirement{
"B": {Name: "B"},
"Z": {Name: "Z"},
"group1": {AnyOf: []string{"A", "C"}},
}
got := flattenAndSortRequirements(by)
if len(got) != 3 {
t.Fatalf("len: got %d, want 3", len(got))
}
// Singles first, then groups.
if got[0].Name == "" {
t.Error("first item should be a single")
}
if got[1].Name == "" {
t.Error("second item should be a single")
}
// Within singles, alphabetical.
if got[0].Name != "B" || got[1].Name != "Z" {
t.Errorf("got singles %q, %q, want B, Z", got[0].Name, got[1].Name)
}
// Then groups.
if len(got[2].AnyOf) == 0 {
t.Error("third item should be a group")
}
}
func TestFlattenAndSortRequirements_GroupsSortedByKey_Pure(t *testing.T) {
by := map[string]EnvRequirement{
"B\x00A": {AnyOf: []string{"B", "A"}}, // key already sorted
"A\x00C": {AnyOf: []string{"A", "C"}},
}
got := flattenAndSortRequirements(by)
// After singles, groups sorted by their envRequirementKey.
// "A\x00B" < "A\x00C", so the B/A group (key "A\x00B") comes first.
group0 := got[len(got)-2]
group1 := got[len(got)-1]
if group0.AnyOf[0] != "B" || group1.AnyOf[0] != "A" {
t.Errorf("group order: got %v then %v, want [B,A] then [A,C]", group0.AnyOf, group1.AnyOf)
}
}
func TestFlattenAndSortRequirements_EmptyMap_Pure(t *testing.T) {
by := map[string]EnvRequirement{}
got := flattenAndSortRequirements(by)
if len(got) != 0 {
t.Errorf("empty map: got %d items, want 0", len(got))
}
}
func TestFlattenAndSortRequirements_OnlyGroups_Pure(t *testing.T) {
by := map[string]EnvRequirement{
"X\x00Y": {AnyOf: []string{"X", "Y"}},
}
got := flattenAndSortRequirements(by)
if len(got) != 1 {
t.Fatalf("len: got %d, want 1", len(got))
}
if len(got[0].AnyOf) == 0 {
t.Error("only group expected")
}
}
func TestFlattenAndSortRequirements_Deterministic_Pure(t *testing.T) {
// Run twice; results must be identical.
by := map[string]EnvRequirement{
"C": {Name: "C"},
"A": {Name: "A"},
"B": {Name: "B"},
"Z\x00Y": {AnyOf: []string{"Z", "Y"}},
}
got1 := flattenAndSortRequirements(by)
got2 := flattenAndSortRequirements(by)
if len(got1) != len(got2) {
t.Fatalf("len mismatch: %d vs %d", len(got1), len(got2))
}
for i := range got1 {
if got1[i].Name != got2[i].Name || len(got1[i].AnyOf) != len(got2[i].AnyOf) {
t.Errorf("item %d differs: %+v vs %+v", i, got1[i], got2[i])
}
}
}
func TestFlattenAndSortRequirements_AllSingles_Pure(t *testing.T) {
by := map[string]EnvRequirement{
"X": {Name: "X"},
"M": {Name: "M"},
"A": {Name: "A"},
}
got := flattenAndSortRequirements(by)
if len(got) != 3 {
t.Fatalf("len: got %d, want 3", len(got))
}
names := make([]string, len(got))
for i, r := range got {
names[i] = r.Name
}
// Must be sorted alphabetically.
if !sort.IsSorted(sort.StringSlice(names)) {
t.Errorf("not sorted: %v", names)
}
}
// ─────────────────────────────────────────────────────────────────────────────
// collectOrgEnv tests
// ─────────────────────────────────────────────────────────────────────────────
func TestCollectOrgEnv_EmptyTemplate_Pure(t *testing.T) {
tmpl := &OrgTemplate{}
req, rec := collectOrgEnv(tmpl)
if len(req) != 0 || len(rec) != 0 {
t.Errorf("empty template: got req=%d rec=%d, want 0,0", len(req), len(rec))
}
}
func TestCollectOrgEnv_SingleRequired_Pure(t *testing.T) {
tmpl := &OrgTemplate{
RequiredEnv: []EnvRequirement{{Name: "API_KEY"}},
}
req, rec := collectOrgEnv(tmpl)
if len(req) != 1 {
t.Errorf("req count: got %d, want 1", len(req))
}
if len(rec) != 0 {
t.Errorf("rec count: got %d, want 0", len(rec))
}
if req[0].Name != "API_KEY" {
t.Errorf("req[0].Name: got %q, want %q", req[0].Name, "API_KEY")
}
}
func TestCollectOrgEnv_SingleRecommended_Pure(t *testing.T) {
tmpl := &OrgTemplate{
RecommendedEnv: []EnvRequirement{{Name: "DEBUG_MODE"}},
}
req, rec := collectOrgEnv(tmpl)
if len(req) != 0 {
t.Errorf("req count: got %d, want 0", len(req))
}
if len(rec) != 1 {
t.Errorf("rec count: got %d, want 1", len(rec))
}
}
func TestCollectOrgEnv_RequiredWinsOverRecommended_Pure(t *testing.T) {
// Same env name in both tiers → appears only in required.
tmpl := &OrgTemplate{
RequiredEnv: []EnvRequirement{{Name: "SHARED_KEY"}},
RecommendedEnv: []EnvRequirement{{Name: "SHARED_KEY"}},
}
req, rec := collectOrgEnv(tmpl)
if len(req) != 1 || len(rec) != 0 {
t.Errorf("got req=%d rec=%d, want 1,0", len(req), len(rec))
}
}
func TestCollectOrgEnv_StrictDropsAnyOf_Pure(t *testing.T) {
// Single required + any-of group containing that name → any-of dropped.
tmpl := &OrgTemplate{
RequiredEnv: []EnvRequirement{{Name: "API_KEY"}},
RecommendedEnv: []EnvRequirement{{AnyOf: []string{"API_KEY", "FALLBACK_KEY"}}},
}
req, rec := collectOrgEnv(tmpl)
if len(req) != 1 || req[0].Name != "API_KEY" {
t.Errorf("req: got %+v", req)
}
if len(rec) != 0 {
t.Errorf("rec: got %d, want 0 (any-of dropped by strict required)", len(rec))
}
}
func TestCollectOrgEnv_AnyOfGroup_Pure(t *testing.T) {
tmpl := &OrgTemplate{
RequiredEnv: []EnvRequirement{{AnyOf: []string{"KEY_A", "KEY_B"}}},
}
req, rec := collectOrgEnv(tmpl)
if len(req) != 1 {
t.Fatalf("req count: got %d, want 1", len(req))
}
if len(req[0].AnyOf) != 2 {
t.Errorf("req[0].AnyOf: got %v, want [KEY_A, KEY_B]", req[0].AnyOf)
}
_ = rec // may be unused but call is valid
}
func TestCollectOrgEnv_NestedWorkspace_Pure(t *testing.T) {
tmpl := &OrgTemplate{
RequiredEnv: []EnvRequirement{{Name: "ORG_KEY"}},
Workspaces: []OrgWorkspace{
{
Name: "child-ws",
RequiredEnv: []EnvRequirement{{Name: "CHILD_KEY"}},
RecommendedEnv: []EnvRequirement{{Name: "CHILD_RECOM"}},
},
},
}
req, rec := collectOrgEnv(tmpl)
// All 3 required names should appear (dedupped).
if len(req) != 2 {
t.Errorf("req count: got %d, want 2", len(req))
}
if len(rec) != 1 {
t.Errorf("rec count: got %d, want 1", len(rec))
}
names := make(map[string]bool)
for _, r := range req {
names[r.Name] = true
}
if !names["ORG_KEY"] || !names["CHILD_KEY"] {
t.Errorf("req names: got %v", names)
}
}
func TestCollectOrgEnv_InvalidEnvNameFiltered_Pure(t *testing.T) {
// Invalid names (lowercase, empty) are silently dropped.
tmpl := &OrgTemplate{
RequiredEnv: []EnvRequirement{
{Name: "VALID_KEY"},
{Name: "ALSO_VALID"},
{Name: "invalid-name"},
{Name: ""},
},
}
req, rec := collectOrgEnv(tmpl)
if len(req) != 2 {
t.Errorf("got %d req, want 2 (lowercase and empty filtered)", len(req))
}
_ = rec
}
func TestCollectOrgEnv_DedupSameMembers_Pure(t *testing.T) {
// Same members declared twice → deduplicated.
tmpl := &OrgTemplate{
RequiredEnv: []EnvRequirement{
{AnyOf: []string{"A", "B"}},
{AnyOf: []string{"B", "A"}}, // same set, reversed
},
}
req, rec := collectOrgEnv(tmpl)
if len(req) != 1 {
t.Errorf("dedup failed: got %d req, want 1", len(req))
}
_ = rec
}
func TestCollectOrgEnv_RecDedupedByStrictRequired_Pure(t *testing.T) {
// Strict required drops any-of groups in recommended tier too.
tmpl := &OrgTemplate{
RequiredEnv: []EnvRequirement{{Name: "STRICT"}},
RecommendedEnv: []EnvRequirement{{AnyOf: []string{"STRICT", "OTHER"}}},
}
req, rec := collectOrgEnv(tmpl)
if len(rec) != 0 {
t.Errorf("rec should be empty (strict required prunes recommended any-of): got %d", len(rec))
}
_ = req
}
func TestCollectOrgEnv_RecursiveChildren_Pure(t *testing.T) {
tmpl := &OrgTemplate{
RequiredEnv: []EnvRequirement{{Name: "ROOT"}},
Workspaces: []OrgWorkspace{
{
Name: "parent",
RequiredEnv: []EnvRequirement{{Name: "PARENT"}, {Name: "PARENT2"}},
Children: []OrgWorkspace{
{
Name: "grandchild",
RequiredEnv: []EnvRequirement{{Name: "GRANDCHILD"}},
},
},
},
},
}
req, rec := collectOrgEnv(tmpl)
if len(req) != 4 {
t.Errorf("got %d required, want 4 (ROOT + PARENT + PARENT2 + GRANDCHILD)", len(req))
}
_ = rec
}
// ─────────────────────────────────────────────────────────────────────────────
// countWorkspaces tests
// ─────────────────────────────────────────────────────────────────────────────
func TestCountWorkspaces_Empty_Pure(t *testing.T) {
if n := countWorkspaces(nil); n != 0 {
t.Errorf("nil: got %d, want 0", n)
}
if n := countWorkspaces([]OrgWorkspace{}); n != 0 {
t.Errorf("empty: got %d, want 0", n)
}
}
func TestCountWorkspaces_Flat_Pure(t *testing.T) {
ws := []OrgWorkspace{
{Name: "a"}, {Name: "b"},
}
if n := countWorkspaces(ws); n != 2 {
t.Errorf("flat: got %d, want 2", n)
}
}
func TestCountWorkspaces_Nested_Pure(t *testing.T) {
ws := []OrgWorkspace{
{Name: "a", Children: []OrgWorkspace{
{Name: "b", Children: []OrgWorkspace{
{Name: "c"},
}},
{Name: "d"},
}},
}
if n := countWorkspaces(ws); n != 4 {
t.Errorf("nested: got %d, want 4", n)
}
}