package resolution import ( "strings" "testing" mcat "git.novox.be/novox/mesh-sdk/go/catalogue" ) func userResource(fields map[string]any) map[string]any { r := map[string]any{"id": "operator", "type": "user", "name": "op"} for k, v := range fields { r[k] = v } return r } func TestAShellAndAGroupOnOneAccountFromTwoModulesResolve(t *testing.T) { zsh := mcat.Manifest{Module: "zsh", Resources: []map[string]any{userResource(map[string]any{"shell": "/usr/bin/zsh"})}} docker := mcat.Manifest{Module: "docker", Resources: []map[string]any{userResource(map[string]any{"groups": []any{"docker"}})}} other := mcat.Manifest{Module: "media", Resources: []map[string]any{userResource(map[string]any{"groups": []any{"video"}})}} if problems := checkResources([]mcat.Manifest{zsh, docker, other}); len(problems) != 0 { t.Fatalf("a shell and two modules' groups on one account were refused: %v", problems) } if _, err := Resolve(shelf(zsh, docker, other), []string{"zsh", "docker", "media"}, workstation(), mcat.World{}); err != nil { t.Fatalf("the three did not resolve together: %v", err) } } func TestTwoModulesSettingOneAccountsShellOrHomeAreRefused(t *testing.T) { for _, field := range []string{"shell", "home"} { a := mcat.Manifest{Module: "zsh", Resources: []map[string]any{userResource(map[string]any{field: "/one"})}} b := mcat.Manifest{Module: "fish", Resources: []map[string]any{userResource(map[string]any{field: "/two", "groups": []any{"x"}})}} problems := checkResources([]mcat.Manifest{a, b}) want := `zsh and fish both set the ` + field + ` of the user "op"` if len(problems) != 1 || !strings.Contains(problems[0], want) { t.Errorf("two modules setting %s gave %v, want %q", field, problems, want) } } } // An account a module declares only to put in a group is applied where it is written, after the package // that makes the group (novox/hq ADR 0252); one that says how it logs in still goes first (issue 213). func TestAnAccountOnlyGivenGroupsKeepsItsWrittenPlace(t *testing.T) { compose := func(raw string) []map[string]any { t.Helper() m, err := decodeForTest([]byte(raw)) if err != nil { t.Fatal(err) } out, err := Declaration(mcat.Resolution{Node: "workstation", Modules: []mcat.Manifest{m}}, mcat.Rendering{}) if err != nil { t.Fatal(err) } return out } out := compose(`{"module": "lights", "version": "1", "resources": [ {"id": "daemon", "type": "package", "package": "lights-daemon"}, {"id": "account", "type": "user", "name": "op", "groups": ["lights"]} ]}`) if pkg, acct := indexOf(out, "lights.daemon"), indexOf(out, "lights.account"); pkg < 0 || acct < pkg { t.Fatalf("the account (%d) is not after the package that makes its group (%d): %v", acct, pkg, out) } out = compose(`{"module": "shell", "version": "1", "resources": [ {"id": "package", "type": "package", "package": "zsh"}, {"id": "login", "type": "user", "name": "op", "shell": "/usr/bin/zsh", "groups": ["wheel"]} ]}`) if pkg, acct := indexOf(out, "shell.package"), indexOf(out, "shell.login"); acct < 0 || acct > pkg { t.Fatalf("an account with a shell is no longer first (%d, package %d): %v", acct, pkg, out) } }