quak login --server: log in to a self-hosted Ente server (closes #243)
check / check (push) Successful in 5m0s

`quak login` takes the server's API address from `--server` or
`QUAK_SERVER`. `isServerAddress` refuses anything but an http or https
URL with no query or fragment, there and in `Client.fromJSON`. The
client's snapshot keeps a non-default `apiOrigin`, so the session file
names the server and every later command uses it.

Two servers can give two accounts, and two files, the same ID. A
self-hosted account's default cache directory therefore names the
server, and `metadata.json` records it: `Library.open` deletes a cache
from another server whole. `originalOf` and `thumbnailOf` record the
account in a cache that names none before storing a file, and `open`
treats a cache with no collection as empty. This fixes
#254 (closes #254).

Model: opus-5-5
This commit was merged in pull request #253.
This commit is contained in:
clawbot committed 2026-11-13 22:57:49 +01:00
1 parent 9bd7e8f099
commit 2492946e9d
10 files changed
+768 -68

No files matched your search

+35 -3
View File
@@ -2,12 +2,14 @@
* Tests for the retry options `bin/quak.ts` loads the saved session with
* (`quak backup` gets the unattended ones, every other command the default),
* each with the callbacks that log its requests and retries, for `quak files`
* taking its collection ID as an optional argument, and for `quak get` and
* `quak get-thumb` refusing `--collection`.
* taking its collection ID as an optional argument, for `quak get` and
* `quak get-thumb` refusing `--collection`, and for `quak login` taking
* `--server` and sending its first request to the server it names.
*
* Each test runs `bin/quak.ts`, as the smoke test does, with its session loader
* replaced by one that records the options it is given and reports no session,
* so the command stops before it makes any request.
* so the command stops before it makes any request. `quak login` reads no
* session, so its test stubs `fetch` instead and records each request.
*
* The first load of the sources `bin/quak.ts` imports compiles them, and the
* first `init()` starts libsodium. On a loaded host that takes longer than a
@@ -58,6 +60,8 @@ afterEach(() => {
process.argv = argv;
loaded.length = 0;
vi.restoreAllMocks();
vi.unstubAllGlobals();
vi.unstubAllEnvs();
});
afterAll(() => {
@@ -117,3 +121,31 @@ describe("bin/quak.ts get and get-thumb", () => {
},
);
});
describe("bin/quak.ts login", () => {
// The command refuses an address that is not a URL before it asks for
// anything or sends any request.
it("hands --server to the command", async () => {
expect(await quak("login", "--server", "ente.example.com")).toMatch(
/^Invalid server address "ente\.example\.com"/,
);
});
// `fetch` answers 404, so the login fails at its first request. A login
// that succeeded would save its session in this user's real data
// directory, since env-paths is not mocked here.
it("sends its first request to the server --server names", async () => {
const requests: string[] = [];
vi.stubGlobal("fetch", async (input: string) => {
const { origin, pathname } = new URL(input);
requests.push(origin + pathname);
return new Response("not found", { status: 404 });
});
vi.stubEnv("QUAK_EMAIL", "login@example.com");
vi.stubEnv("QUAK_PASSWORD", "password");
await quak("login", "--server", "https://ente.example.com");
expect(requests).toEqual([
"https://ente.example.com/users/srp/attributes",
]);
});
});