Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

quickshell/nuguland/vault-runtime.test.mjs

Raw
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { copyFileSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync, existsSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";

// Real QML Process/FileView lifecycle, isolated from the desktop, clipboard and user settings.
const directory = mkdtempSync(join(tmpdir(), "nuguland-vault-runtime-test-"));
const profile = {
    name: "personal", serverUrl: "https://vault.example.com", account: "initial@example.com",
    unlockTimeoutSeconds: 900, clipboardTimeoutSeconds: 30,
};
try {
    for (const file of ["Vault.qml", "vault.js", "VaultPanel.qml", "Theme.qml", "NuguText.qml", "PopupCard.qml", "PopupButton.qml", "PopupTextField.qml", "PopupToolTip.qml", "SetupField.qml"])
        copyFileSync(new URL(file, import.meta.url), join(directory, file));
    mkdirSync(join(directory, "state"));
    mkdirSync(join(directory, "vault-native"));
    writeFileSync(join(directory, "state", "vault.json"), JSON.stringify(profile));
    writeFileSync(join(directory, "Clipboard.qml"), `import QtQuick
Item {
    signal vaultCapturePaused
    function pauseForVaultCopy(seconds) {}
    function resumeAfterVaultCopy() {}
}
`);
    writeFileSync(join(directory, "old.cjs"), `const fs = require("node:fs");
const lines = require("node:readline").createInterface({ input: process.stdin });
lines.on("line", line => {
    if (JSON.parse(line).op === "configure-lock") {
        fs.writeFileSync(${JSON.stringify(join(directory, "obsolete-request"))}, "rejected");
        console.log(JSON.stringify({ event: "protocol-error" }));
    }
});
`);
    const binary = resolve("vault-native/target/debug/nuguland-vault");
    writeFileSync(join(directory, "vault-native", "run.sh"), `#!/bin/sh
printf x >> '${directory}/launches'
if [ ! -f '${directory}/old-started' ]; then
    touch '${directory}/old-started'
    exec node '${directory}/old.cjs'
fi
exec '${binary}'
`, { mode: 0o700 });
    writeFileSync(join(directory, "shell.qml"), `//@ pragma StateDir ${directory}/state
import QtQuick
import Quickshell
import Quickshell.Io
ShellRoot {
    id: test
    property int phase: 0
    property string savedAccount: ""
    property var retainedField: null
    property var fixtureDetail: ({ event: "detail", id: "fixture", fields: [{ id: "login.password", label: "password", value: "synthetic", sensitive: true, kind: "text" }], totp: { current: "123456", next: "654321", remaining: 1, currentSlot: 0, nextSlot: 1 } })
    function find(item, name) {
        if (item.objectName === name) return item;
        for (const child of item.children) {
            const found = find(child, name);
            if (found) return found;
        }
        return null;
    }
    Clipboard { id: clipboard }
    Vault { id: vault; clipboardService: clipboard }
    Theme { id: theme }
    Window {
        visible: true
        width: 980
        height: 620
        VaultPanel { id: panel; anchors.fill: parent; theme: theme; vaultService: vault }
    }
    FileView {
        id: saved
        path: vault.configPath
        watchChanges: true
        onFileChanged: reload()
        onLoaded: test.savedAccount = JSON.parse(text()).account
    }
    Timer {
        interval: 20
        running: true
        repeat: true
        onTriggered: {
            if (vault.setupError) {
                console.error("vault runtime test phase " + test.phase + ": " + vault.setupError);
                Qt.quit();
            } else if (test.phase === 0 && !vault.setupSaving && vault.profile.account === "initial@example.com") {
                vault.beginSetup();
                test.phase = 1;
            } else if (test.phase === 1 && !vault.lockPending) {
                vault.setupUrlDraft = "other.example.com";
                vault.setupAccountDraft = "saved@example.com";
                vault.saveSetup();
                test.phase = 2;
            } else if (test.phase === 2 && !vault.setupSaving && !vault.setupEditing
                       && vault.status === "locked" && test.savedAccount === "saved@example.com") {
                vault.beginSetup();
                test.phase = 3;
            } else if (test.phase === 3 && !vault.lockPending) {
                vault.setupUrlDraft = "http://invalid.example.com";
                vault.testConnection();
                test.phase = 4;
            } else if (test.phase === 4 && !vault.connectionBusy && vault.connectionResult.indexOf("https") >= 0) {
                if (vault.profile.serverUrl !== "https://other.example.com" || test.savedAccount !== "saved@example.com")
                    console.error("connection test changed saved settings");
                else {
                    vault.handleLine(JSON.stringify({ event: "state", status: "second-factor", items: [],
                        challenge: { providers: [{ id: 1, label: "email code", codeSupported: true }] } }));
                    vault.panelOpen = true;
                    vault.selectSecondFactor(1);
                    vault.secondFactorDraft = "123";
                    vault.panelOpen = false;
                    test.phase = 5;
                }
            } else if (test.phase === 5) {
                vault.panelOpen = true;
                if (vault.status !== "second-factor" || vault.secondFactorProvider !== 1 || vault.secondFactorDraft !== "123"
                    || vault.secondFactorHint().indexOf("check your inbox") < 0)
                    console.error("closing lost verification state");
                else
                    console.info("vault runtime test: saved");
                vault.cancelUnlock();
                if (vault.secondFactorDraft !== "")
                    console.error("cancel retained verification code");
                test.phase = 6;
            } else if (test.phase === 6 && !vault.lockPending && vault.status === "locked") {
                vault.cancelSetup();
                vault.status = "unlocked";
                vault.selectedId = "fixture";
                vault.handleLine(JSON.stringify(test.fixtureDetail));
                test.phase = 7;
            } else if (test.phase === 7) {
                test.retainedField = test.find(panel, "vault-field-login.password");
                if (!test.retainedField) {
                    console.error("fixture field delegate missing");
                    Qt.quit();
                    return;
                }
                test.retainedField.forceActiveFocus();
                const update = Object.assign({}, test.fixtureDetail, { totp: { current: "654321", next: "987654", remaining: 30, currentSlot: 1, nextSlot: 2 } });
                vault.handleLine(JSON.stringify(update));
                test.phase = 8;
            } else if (test.phase === 8) {
                if (test.find(panel, "vault-field-login.password") !== test.retainedField || !test.retainedField.activeFocus)
                    console.error("TOTP rollover replaced the field delegate or lost keyboard focus");
                else
                    console.info("vault runtime test: field focus retained");
                vault.lock();
                test.phase = 9;
            } else if (test.phase === 9 && !vault.lockPending) {
                if (!test.find(panel, "vaultMasterPassword").input.activeFocus)
                    console.error("locking failed to focus the master password input");
                else
                    console.info("vault runtime test: unlock focused");
                Qt.quit();
            }
        }
    }
}
`);
    mkdirSync(join(directory, "runtime"), { mode: 0o700 });
    const environment = {
        ...process.env,
        QT_QPA_PLATFORM: "offscreen", QT_QUICK_BACKEND: "software", QT_QPA_PLATFORMTHEME: "",
        QT_NO_XDG_DESKTOP_PORTAL: "1", QS_NO_RELOAD_POPUP: "1",
        XDG_STATE_HOME: join(directory, "state"), XDG_CACHE_HOME: join(directory, "cache"),
        XDG_DATA_HOME: join(directory, "data"), XDG_RUNTIME_DIR: join(directory, "runtime"),
    };
    delete environment.WAYLAND_DISPLAY;
    delete environment.DISPLAY;
    const result = spawnSync("qs", ["--no-color", "-p", join(directory, "shell.qml")], {
        cwd: directory,
        env: environment,
        encoding: "utf8", timeout: 15_000,
    });
    const output = result.stdout + result.stderr + "\nlaunches: " + (existsSync(join(directory, "launches")) ? readFileSync(join(directory, "launches"), "utf8") : "none");
    assert.equal(result.error, undefined, output);
    assert.equal(result.status, 0, output);
    assert.doesNotMatch(output, /WARN|ERROR|Error:|TypeError|ReferenceError/, output);
    assert.match(output, /vault runtime test: saved/);
    assert.match(output, /vault runtime test: field focus retained/);
    assert.match(output, /vault runtime test: unlock focused/);
    assert.equal(existsSync(join(directory, "obsolete-request")), false, "old protocol must not receive configuration requests");
    assert.equal(readFileSync(join(directory, "launches"), "utf8"), "xxxx", "replace obsolete boundary, replace on save, test in a separate boundary");
    assert.deepEqual(JSON.parse(readFileSync(join(directory, "state", "vault.json"), "utf8")), {
        ...profile, serverUrl: "https://other.example.com", account: "saved@example.com",
    });
    console.log("vault-runtime.test.mjs: ok");
} finally {
    rmSync(directory, { recursive: true, force: true });
}