@homeflare/alchemy 0.27.2 → 0.27.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homeflare/alchemy",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.3",
|
|
4
4
|
"description": "Custom Alchemy providers for Cloudflare, Proxmox, OpenBao, Forgejo, Talos, launchd and Caddy, and a verifier that proves adoptions are no-ops.",
|
|
5
5
|
"homepage": "https://github.com/taslabs-net/homeflare-kit/tree/main/packages/alchemy#readme",
|
|
6
6
|
"bugs": "https://github.com/taslabs-net/homeflare-kit/issues",
|
|
@@ -93,6 +93,9 @@
|
|
|
93
93
|
"publishConfig": {
|
|
94
94
|
"access": "public"
|
|
95
95
|
},
|
|
96
|
+
"dependencies": {
|
|
97
|
+
"@distilled.cloud/proxmox": "npm:@homeflare/distilled-proxmox@0.2.0"
|
|
98
|
+
},
|
|
96
99
|
"peerDependencies": {
|
|
97
100
|
"@distilled.cloud/cloudflare": "1.0.0-rc.12",
|
|
98
101
|
"@distilled.cloud/forgejo": "1.0.0-rc.12",
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exercises `@distilled.cloud/proxmox`'s `awaitTask` (the async-task
|
|
3
|
+
* 200-trap — see that package's `src/protocol.ts` header) against a FAKE
|
|
4
|
+
* `HttpClient`, no live PVE node involved.
|
|
5
|
+
*
|
|
6
|
+
* ⚠️ THIS PACKAGE IS CONSUMED VIA THE INTERIM-PACKAGE ALIAS
|
|
7
|
+
* (`packages/alchemy/package.json`'s `dependencies`:
|
|
8
|
+
* `"@distilled.cloud/proxmox": "npm:@homeflare/distilled-proxmox@0.2.0"`)
|
|
9
|
+
* — see `packages/alchemy/docs/distilled-interim.md`. Nothing in
|
|
10
|
+
* `packages/alchemy/src/proxmox/*` (the existing 98-resource family) is
|
|
11
|
+
* touched or migrated onto it here; this file is standalone proof the
|
|
12
|
+
* package works end to end, ahead of migrating any hand-written resource
|
|
13
|
+
* onto it (a later PR).
|
|
14
|
+
*/
|
|
15
|
+
import { describe, expect, test } from 'bun:test';
|
|
16
|
+
import * as Effect from 'effect/Effect';
|
|
17
|
+
import * as Layer from 'effect/Layer';
|
|
18
|
+
import * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
19
|
+
import * as HttpClientResponse from 'effect/unstable/http/HttpClientResponse';
|
|
20
|
+
import * as Proxmox from '@distilled.cloud/proxmox';
|
|
21
|
+
|
|
22
|
+
const credentials = Proxmox.credentials({
|
|
23
|
+
tokenId: 'root@pam!test',
|
|
24
|
+
secret: 'test-secret',
|
|
25
|
+
baseUrl: 'https://pve.test:8006',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/** A fake PVE that answers `running` (no `exitstatus`) `pendingPolls` times, then `finalBody`. */
|
|
29
|
+
const fakePve = (pendingPolls: number, finalBody: Record<string, unknown>) => {
|
|
30
|
+
let calls = 0;
|
|
31
|
+
return Layer.succeed(
|
|
32
|
+
HttpClient.HttpClient,
|
|
33
|
+
HttpClient.make((request) =>
|
|
34
|
+
Effect.sync(() => {
|
|
35
|
+
calls++;
|
|
36
|
+
const body =
|
|
37
|
+
calls <= pendingPolls
|
|
38
|
+
? {
|
|
39
|
+
status: 'running',
|
|
40
|
+
id: 'qmstart',
|
|
41
|
+
node: 'pve1',
|
|
42
|
+
pid: 100,
|
|
43
|
+
pstart: 1,
|
|
44
|
+
starttime: 1,
|
|
45
|
+
type: 'qmstart',
|
|
46
|
+
upid: 'UPID:pve1:...',
|
|
47
|
+
user: 'root@pam',
|
|
48
|
+
}
|
|
49
|
+
: finalBody;
|
|
50
|
+
return HttpClientResponse.fromWeb(
|
|
51
|
+
request,
|
|
52
|
+
new Response(JSON.stringify({ data: body }), { status: 200 }),
|
|
53
|
+
);
|
|
54
|
+
}),
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const runAwaitTask = (pendingPolls: number, finalBody: Record<string, unknown>) =>
|
|
60
|
+
Effect.runPromise(
|
|
61
|
+
Proxmox.awaitTask(
|
|
62
|
+
{ node: 'pve1', upid: 'UPID:pve1:00000001:00000001:00000001:qmstart:100:root@pam:' },
|
|
63
|
+
{ pollInterval: '5 millis' },
|
|
64
|
+
).pipe(Effect.provide(Layer.mergeAll(fakePve(pendingPolls, finalBody), credentials))),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const OK_STATUS = {
|
|
68
|
+
status: 'stopped',
|
|
69
|
+
exitstatus: 'OK',
|
|
70
|
+
id: 'qmstart',
|
|
71
|
+
node: 'pve1',
|
|
72
|
+
pid: 100,
|
|
73
|
+
pstart: 1,
|
|
74
|
+
starttime: 1,
|
|
75
|
+
type: 'qmstart',
|
|
76
|
+
upid: 'UPID:pve1:...',
|
|
77
|
+
user: 'root@pam',
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
describe("awaitTask (against a fake PVE, per protocol.ts's async-task trap)", () => {
|
|
81
|
+
test("resolves once exitstatus is exactly OK, after polling through 'running'", async () => {
|
|
82
|
+
const result = await runAwaitTask(2, OK_STATUS);
|
|
83
|
+
expect(result.exitstatus).toBe('OK');
|
|
84
|
+
expect(result.status).toBe('stopped');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('resolves immediately when the task is already done on the first poll', async () => {
|
|
88
|
+
const result = await runAwaitTask(0, OK_STATUS);
|
|
89
|
+
expect(result.exitstatus).toBe('OK');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('fails with ProxmoxTaskFailed for any exitstatus other than exactly OK', async () => {
|
|
93
|
+
const failed = await Effect.runPromise(
|
|
94
|
+
Proxmox.awaitTask(
|
|
95
|
+
{ node: 'pve1', upid: 'UPID:pve1:00000001:00000001:00000001:qmstart:100:root@pam:' },
|
|
96
|
+
{ pollInterval: '5 millis' },
|
|
97
|
+
).pipe(
|
|
98
|
+
Effect.provide(
|
|
99
|
+
Layer.mergeAll(fakePve(0, { ...OK_STATUS, exitstatus: 'job errors' }), credentials),
|
|
100
|
+
),
|
|
101
|
+
Effect.flip,
|
|
102
|
+
),
|
|
103
|
+
);
|
|
104
|
+
expect(failed).toBeInstanceOf(Proxmox.ProxmoxTaskFailed);
|
|
105
|
+
expect(failed).toMatchObject({ node: 'pve1', exitstatus: 'job errors' });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ⛔ "OK (warnings)" is NOT a success — see task.ts's exact-equality note.
|
|
109
|
+
test("fails (does not swallow) 'OK (warnings)'", async () => {
|
|
110
|
+
const failed = await Effect.runPromise(
|
|
111
|
+
Proxmox.awaitTask(
|
|
112
|
+
{ node: 'pve1', upid: 'UPID:pve1:00000001:00000001:00000001:qmstart:100:root@pam:' },
|
|
113
|
+
{ pollInterval: '5 millis' },
|
|
114
|
+
).pipe(
|
|
115
|
+
Effect.provide(
|
|
116
|
+
Layer.mergeAll(fakePve(0, { ...OK_STATUS, exitstatus: 'OK (warnings)' }), credentials),
|
|
117
|
+
),
|
|
118
|
+
Effect.flip,
|
|
119
|
+
),
|
|
120
|
+
);
|
|
121
|
+
expect(failed).toBeInstanceOf(Proxmox.ProxmoxTaskFailed);
|
|
122
|
+
expect(failed).toMatchObject({ exitstatus: 'OK (warnings)' });
|
|
123
|
+
});
|
|
124
|
+
});
|