Add advanced synchronize dialog and APK tooling
This commit is contained in:
+153
-1
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -12,8 +13,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gioui.org/layout"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/unit"
|
||||
"gioui.org/widget"
|
||||
|
||||
@@ -678,6 +679,145 @@ func TestUIRemoteSaveConflictShowsVisibleErrorAndKeepsDirtyState(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIAdvancedSynchronizeFromLocalMergesIntoCurrentVault(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key := vault.MasterKey{Password: "correct horse battery staple"}
|
||||
currentPath := filepath.Join(t.TempDir(), "current.kdbx")
|
||||
otherPath := filepath.Join(t.TempDir(), "other.kdbx")
|
||||
|
||||
writeKDBXMainTestFile(t, currentPath, vault.Model{
|
||||
Entries: []vault.Entry{{
|
||||
ID: "entry-current",
|
||||
Title: "Vault Console",
|
||||
Username: "dannyocean",
|
||||
Password: "token-current",
|
||||
URL: "https://vault.crew.example.invalid",
|
||||
Path: []string{"Root", "Internet"},
|
||||
}},
|
||||
}, key)
|
||||
writeKDBXMainTestFile(t, otherPath, vault.Model{
|
||||
Entries: []vault.Entry{{
|
||||
ID: "entry-other",
|
||||
Title: "Bellagio",
|
||||
Username: "rustyryan",
|
||||
Password: "token-other",
|
||||
URL: "https://bellagio.example.invalid",
|
||||
Path: []string{"Root", "Internet"},
|
||||
}},
|
||||
}, key)
|
||||
|
||||
u := newUIWithSession("desktop", &session.Manager{})
|
||||
u.masterPassword.SetText(key.Password)
|
||||
u.vaultPath.SetText(currentPath)
|
||||
if err := u.openVaultAction(); err != nil {
|
||||
t.Fatalf("openVaultAction() error = %v", err)
|
||||
}
|
||||
|
||||
u.openAdvancedSyncDialog()
|
||||
u.syncDirection = syncDirectionPull
|
||||
u.syncSourceMode = syncSourceLocal
|
||||
u.syncLocalPath.SetText(otherPath)
|
||||
if err := u.advancedSyncAction(); err != nil {
|
||||
t.Fatalf("advancedSyncAction() error = %v", err)
|
||||
}
|
||||
|
||||
var reopened session.Manager
|
||||
if err := reopened.Open(currentPath, key); err != nil {
|
||||
t.Fatalf("reopen Open(current) error = %v", err)
|
||||
}
|
||||
model, err := reopened.Current()
|
||||
if err != nil {
|
||||
t.Fatalf("reopened Current() error = %v", err)
|
||||
}
|
||||
if got := len(model.EntriesInPath([]string{"Root", "Internet"})); got != 2 {
|
||||
t.Fatalf("len(EntriesInPath(Root/Internet)) = %d, want 2", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIAdvancedSynchronizeToRemoteWritesMergedVaultToTarget(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key := vault.MasterKey{Password: "correct horse battery staple"}
|
||||
currentPath := filepath.Join(t.TempDir(), "current.kdbx")
|
||||
writeKDBXMainTestFile(t, currentPath, vault.Model{
|
||||
Entries: []vault.Entry{{
|
||||
ID: "entry-current",
|
||||
Title: "Vault Console",
|
||||
Username: "dannyocean",
|
||||
Password: "token-current",
|
||||
URL: "https://vault.crew.example.invalid",
|
||||
Path: []string{"Root", "Internet"},
|
||||
}},
|
||||
}, key)
|
||||
|
||||
var remoteBytes bytes.Buffer
|
||||
if err := vault.SaveKDBXWithKey(&remoteBytes, vault.Model{
|
||||
Entries: []vault.Entry{{
|
||||
ID: "entry-remote",
|
||||
Title: "Bellagio",
|
||||
Username: "rustyryan",
|
||||
Password: "token-remote",
|
||||
URL: "https://bellagio.example.invalid",
|
||||
Path: []string{"Root", "Internet"},
|
||||
}},
|
||||
}, key); err != nil {
|
||||
t.Fatalf("SaveKDBXWithKey(remote) error = %v", err)
|
||||
}
|
||||
|
||||
etag := "\"v1\""
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
w.Header().Set("ETag", etag)
|
||||
_, _ = w.Write(remoteBytes.Bytes())
|
||||
case http.MethodPut:
|
||||
payload, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadAll(PUT body) error = %v", err)
|
||||
}
|
||||
remoteBytes.Reset()
|
||||
if _, err := remoteBytes.Write(payload); err != nil {
|
||||
t.Fatalf("Write(remoteBytes) error = %v", err)
|
||||
}
|
||||
etag = "\"v2\""
|
||||
w.Header().Set("ETag", etag)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
default:
|
||||
t.Fatalf("unexpected method %s", r.Method)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
u := newUIWithSession("desktop", &session.Manager{})
|
||||
u.masterPassword.SetText(key.Password)
|
||||
u.vaultPath.SetText(currentPath)
|
||||
if err := u.openVaultAction(); err != nil {
|
||||
t.Fatalf("openVaultAction() error = %v", err)
|
||||
}
|
||||
|
||||
u.openAdvancedSyncDialog()
|
||||
u.syncDirection = syncDirectionPush
|
||||
u.syncSourceMode = syncSourceRemote
|
||||
u.syncRemoteBaseURL.SetText(server.URL)
|
||||
u.syncRemotePath.SetText("vaults/other.kdbx")
|
||||
if err := u.advancedSyncAction(); err != nil {
|
||||
t.Fatalf("advancedSyncAction() error = %v", err)
|
||||
}
|
||||
|
||||
var reopened session.Manager
|
||||
if err := reopened.OpenRemote(webdav.Client{BaseURL: server.URL}, "vaults/other.kdbx", key); err != nil {
|
||||
t.Fatalf("OpenRemote(reopened) error = %v", err)
|
||||
}
|
||||
model, err := reopened.Current()
|
||||
if err != nil {
|
||||
t.Fatalf("reopened Current() error = %v", err)
|
||||
}
|
||||
if got := len(model.EntriesInPath([]string{"Root", "Internet"})); got != 2 {
|
||||
t.Fatalf("len(EntriesInPath(Root/Internet)) = %d, want 2", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIMasterKeyInputSupportsKeyFileAndCompositeKeys(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -2839,3 +2979,15 @@ func TestUICurrentMasterKeyReportsUnreadableKeyFile(t *testing.T) {
|
||||
t.Fatalf("currentMasterKey() error = %v, want os.ErrNotExist", err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeKDBXMainTestFile(t *testing.T, path string, model vault.Model, key vault.MasterKey) {
|
||||
t.Helper()
|
||||
|
||||
var encoded bytes.Buffer
|
||||
if err := vault.SaveKDBXWithKey(&encoded, model, key); err != nil {
|
||||
t.Fatalf("SaveKDBXWithKey(%s) error = %v", path, err)
|
||||
}
|
||||
if err := os.WriteFile(path, encoded.Bytes(), 0o600); err != nil {
|
||||
t.Fatalf("WriteFile(%s) error = %v", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user