package catalogue import ( "strings" "testing" ) // Defends novox/hq ADR 0341: a machine's SMB shares are one node seat, node-smb-server, with its verbs // required of every holder, delivering nothing — its clients are devices that are not nodes. func TestTheSMBServerIsANodeSeatWithItsVerbs(t *testing.T) { s, ok := SeatNamed(SMBServerSeat) if !ok { t.Fatalf("%s is not in the mesh's set", SMBServerSeat) } if s.Scope != ScopeNode || s.Decision != "novox/hq ADR 0341" || s.Delivers != "" || s.Replicated { t.Errorf("%s is %+v; a node seat under ADR 0341 that delivers nothing", SMBServerSeat, s) } var got []string for _, v := range s.Serves { got = append(got, v.Name) if v.Optional { t.Errorf("%s.%s is optional; its first holder serves it", SMBServerSeat, v.Name) } if v.Description == "" || v.Input["type"] != "object" || len(v.Replaces) == 0 { t.Errorf("%s.%s has no description, no object schema or says it replaces nothing", SMBServerSeat, v.Name) } } if strings.Join(got, " ") != "shares sessions test reload adopt" { t.Errorf("%s serves %v", SMBServerSeat, got) } } // Taking over the hand-written configuration is a person's act (ADR 0341 clause 7): a dry run unless the // string switch says "true", and it says it keeps a dated copy and refuses to drop a share. func TestTheSMBAdoptIsADryRunUnlessConfirmed(t *testing.T) { s, _ := SeatNamed(SMBServerSeat) for _, v := range s.Serves { if v.Name != "adopt" { continue } props, _ := v.Input["properties"].(map[string]any) confirm, _ := props["confirm"].(map[string]any) if confirm == nil || confirm["enum"] == nil || confirm["type"] != "string" { t.Errorf("adopt has no string confirm switch: %v", v.Input) } for _, says := range []string{"Without confirm, says what it would do and changes nothing", "dated copy", "no share disappears"} { if !strings.Contains(v.Description, says) { t.Errorf("adopt does not say %q: %s", says, v.Description) } } return } t.Error("node-smb-server has no adopt") } // A holder serving every verb holds the seat; a verb too many, one too few or the mesh's scope is refused, // as the catalogue's smb-server module claims it. func TestAHolderOfTheSMBSeatServesEveryVerbAndNothingElse(t *testing.T) { seat, _ := SeatNamed(SMBServerSeat) verbs := []string{"shares", "sessions", "test", "reload", "adopt"} holder := Manifest{Module: "smb-server", Version: "1", Claims: []Claim{{Name: SMBServerSeat, Scope: ScopeNode, Serves: verbs}}} if err := CanHold(holder, seat); err != nil { t.Errorf("smb-server serving every verb is refused: %v", err) } short := holder short.Claims = []Claim{{Name: SMBServerSeat, Scope: ScopeNode, Serves: verbs[:4]}} if err := CanHold(short, seat); err == nil || !strings.Contains(err.Error(), "adopt") { t.Errorf("leaving adopt out was accepted: %v", err) } mesh := holder mesh.Claims = []Claim{{Name: SMBServerSeat, Scope: ScopeMesh, Serves: verbs}} if err := CanHold(mesh, seat); err == nil { t.Error("claiming a node seat at the mesh's scope was accepted") } } // node-nfs-server's adopt clears sharenfs only once the mesh's export covers every client the property // exported to, the local network included (ADR 0341 clause 10), and exports names the local-network grant. func TestTheNFSVerbsSayTheLocalNetworkGrant(t *testing.T) { s, _ := SeatNamed(NFSServerSeat) for _, v := range s.Serves { switch v.Name { case "adopt": if !strings.Contains(v.Description, "covers every client the property exported to") { t.Errorf("nfs adopt does not wait for the export to cover every client: %s", v.Description) } case "exports": if !strings.Contains(v.Description, "local-network") { t.Errorf("nfs exports does not say the local-network grant: %s", v.Description) } } } }