package appstate import ( "encoding/json" "errors" "strings" "testing" "git.julianfamily.org/keepassgo/vault" ) func TestRemoteBindingResolveUsesVaultProfileAndCredentialEntry(t *testing.T) { t.Parallel() model := vault.Model{ Entries: []vault.Entry{ { ID: "tjulian-webdav", Title: "WebDAV Sign-In", Username: "tjulian", Password: "token-1", Path: []string{"Crew", "Internet"}, }, }, RemoteProfiles: []vault.RemoteProfile{ { ID: "family-webdav", Name: "Family Vault", Backend: vault.RemoteBackendWebDAV, BaseURL: "https://dav.example.invalid/remote.php/dav", Path: "files/family/keepass.kdbx", }, }, } binding := RemoteBinding{ LocalVaultPath: "/tmp/family.kdbx", RemoteProfileID: "family-webdav", CredentialEntryID: "tjulian-webdav", SyncMode: SyncModeAutomaticOnOpenSave, } resolved, err := binding.Resolve(model) if err != nil { t.Fatalf("Resolve() error = %v", err) } if got := resolved.Profile.BaseURL; got != "https://dav.example.invalid/remote.php/dav" { t.Fatalf("resolved profile base URL = %q, want remote.php/dav URL", got) } if got := resolved.Profile.Path; got != "files/family/keepass.kdbx" { t.Fatalf("resolved profile path = %q, want files/family/keepass.kdbx", got) } if got := resolved.Credentials.Username; got != "tjulian" { t.Fatalf("resolved credentials username = %q, want tjulian", got) } if got := resolved.Credentials.Password; got != "token-1" { t.Fatalf("resolved credentials password = %q, want token-1", got) } } func TestRemoteBindingResolveFailsWhenVaultReferenceIsMissing(t *testing.T) { t.Parallel() model := vault.Model{ Entries: []vault.Entry{ {ID: "tjulian-webdav", Title: "WebDAV Sign-In"}, }, } _, err := (RemoteBinding{ LocalVaultPath: "/tmp/family.kdbx", RemoteProfileID: "family-webdav", CredentialEntryID: "missing-creds", }).Resolve(model) if !errors.Is(err, vault.ErrRemoteProfileNotFound) { t.Fatalf("Resolve() error = %v, want ErrRemoteProfileNotFound first", err) } model.RemoteProfiles = []vault.RemoteProfile{{ ID: "family-webdav", Name: "Family Vault", Backend: vault.RemoteBackendWebDAV, BaseURL: "https://dav.example.invalid/remote.php/dav", Path: "files/family/keepass.kdbx", }} _, err = (RemoteBinding{ LocalVaultPath: "/tmp/family.kdbx", RemoteProfileID: "family-webdav", CredentialEntryID: "missing-creds", }).Resolve(model) if !errors.Is(err, vault.ErrEntryNotFound) { t.Fatalf("Resolve() error = %v, want ErrEntryNotFound", err) } } func TestRemoteBindingJSONStoresOnlyNonSecretReferences(t *testing.T) { t.Parallel() content, err := json.Marshal(RemoteBinding{ LocalVaultPath: "/tmp/family.kdbx", RemoteProfileID: "family-webdav", CredentialEntryID: "remote-creds-1", SyncMode: SyncModeAutomaticOnOpenSave, }) if err != nil { t.Fatalf("json.Marshal(RemoteBinding) error = %v", err) } text := string(content) for _, disallowed := range []string{"token-1", "password", "username", "baseUrl"} { if strings.Contains(text, disallowed) { t.Fatalf("binding JSON %q unexpectedly contains %q", text, disallowed) } } }