@fatagnus/remote-cmd-relay-convex 1.0.0

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.
@@ -0,0 +1,399 @@
1
+ import { convexTest } from "convex-test";
2
+ import type { Id } from "./_generated/dataModel";
3
+
4
+ // Types for mock data
5
+ export type CommandStatus =
6
+ | "pending"
7
+ | "claimed"
8
+ | "executing"
9
+ | "completed"
10
+ | "failed"
11
+ | "timeout";
12
+ export type TargetType = "local" | "ssh";
13
+ export type Capability = "ssh" | "local_cmd" | "perf_metrics";
14
+ export type CredentialType = "ssh_key" | "password" | "api_key";
15
+ export type StorageMode = "relay_only" | "shared";
16
+ export type ConfigPushType =
17
+ | "credential"
18
+ | "ssh_targets"
19
+ | "allowed_commands"
20
+ | "metrics_interval";
21
+ export type ConfigPushStatus = "pending" | "sent" | "acked" | "failed";
22
+
23
+ // Mock relay assignment data structure
24
+ export interface MockRelayAssignment {
25
+ _id: Id<"relayAssignments">;
26
+ apiKeyId: string;
27
+ machineId: string;
28
+ name: string;
29
+ enabled: boolean;
30
+ lastSeenAt?: number;
31
+ createdBy: string;
32
+ createdAt: number;
33
+ updatedAt: number;
34
+ }
35
+
36
+ // Options for creating a mock relay assignment
37
+ export interface CreateMockRelayAssignmentOptions {
38
+ apiKeyId?: string;
39
+ machineId?: string;
40
+ name?: string;
41
+ enabled?: boolean;
42
+ createdBy?: string;
43
+ }
44
+
45
+ /**
46
+ * Create a mock relay assignment in the test database.
47
+ */
48
+ export async function createMockRelayAssignment(
49
+ t: ReturnType<typeof convexTest>,
50
+ options: CreateMockRelayAssignmentOptions = {}
51
+ ): Promise<MockRelayAssignment> {
52
+ const now = Date.now();
53
+ const apiKeyId = options.apiKeyId ?? `api-key-${now}`;
54
+ const machineId = options.machineId ?? `machine-${now}`;
55
+ const name = options.name ?? "Test Relay";
56
+ const enabled = options.enabled ?? true;
57
+ const createdBy = options.createdBy ?? "test-user";
58
+
59
+ const assignmentId = await t.run(async (ctx) => {
60
+ return await ctx.db.insert("relayAssignments", {
61
+ apiKeyId,
62
+ machineId,
63
+ name,
64
+ enabled,
65
+ createdBy,
66
+ createdAt: now,
67
+ updatedAt: now,
68
+ });
69
+ });
70
+
71
+ return {
72
+ _id: assignmentId as Id<"relayAssignments">,
73
+ apiKeyId,
74
+ machineId,
75
+ name,
76
+ enabled,
77
+ createdBy,
78
+ createdAt: now,
79
+ updatedAt: now,
80
+ };
81
+ }
82
+
83
+ // Mock command data structure
84
+ export interface MockCommand {
85
+ _id: Id<"commandQueue">;
86
+ machineId: string;
87
+ command: string;
88
+ targetType: TargetType;
89
+ targetHost?: string;
90
+ targetPort?: number;
91
+ targetUsername?: string;
92
+ timeoutMs: number;
93
+ status: CommandStatus;
94
+ createdBy: string;
95
+ createdAt: number;
96
+ updatedAt: number;
97
+ }
98
+
99
+ // Options for creating a mock command
100
+ export interface CreateMockCommandOptions {
101
+ machineId?: string;
102
+ command?: string;
103
+ targetType?: TargetType;
104
+ targetHost?: string;
105
+ targetPort?: number;
106
+ targetUsername?: string;
107
+ timeoutMs?: number;
108
+ status?: CommandStatus;
109
+ createdBy?: string;
110
+ claimedBy?: string;
111
+ claimedAt?: number;
112
+ }
113
+
114
+ /**
115
+ * Create a mock command in the test database.
116
+ */
117
+ export async function createMockCommand(
118
+ t: ReturnType<typeof convexTest>,
119
+ options: CreateMockCommandOptions = {}
120
+ ): Promise<MockCommand> {
121
+ const now = Date.now();
122
+ const machineId = options.machineId ?? `machine-${now}`;
123
+ const command = options.command ?? "echo hello";
124
+ const targetType = options.targetType ?? "local";
125
+ const timeoutMs = options.timeoutMs ?? 30000;
126
+ const status = options.status ?? "pending";
127
+ const createdBy = options.createdBy ?? "test-user";
128
+
129
+ const commandId = await t.run(async (ctx) => {
130
+ return await ctx.db.insert("commandQueue", {
131
+ machineId,
132
+ command,
133
+ targetType,
134
+ targetHost: options.targetHost,
135
+ targetPort: options.targetPort ?? (targetType === "ssh" ? 22 : undefined),
136
+ targetUsername: options.targetUsername,
137
+ timeoutMs,
138
+ status,
139
+ claimedBy: options.claimedBy,
140
+ claimedAt: options.claimedAt,
141
+ createdBy,
142
+ createdAt: now,
143
+ updatedAt: now,
144
+ });
145
+ });
146
+
147
+ return {
148
+ _id: commandId as Id<"commandQueue">,
149
+ machineId,
150
+ command,
151
+ targetType,
152
+ targetHost: options.targetHost,
153
+ targetPort: options.targetPort,
154
+ targetUsername: options.targetUsername,
155
+ timeoutMs,
156
+ status,
157
+ createdBy,
158
+ createdAt: now,
159
+ updatedAt: now,
160
+ };
161
+ }
162
+
163
+ // Mock config push data structure
164
+ export interface MockConfigPush {
165
+ _id: Id<"configPushQueue">;
166
+ relayId: string;
167
+ pushType: ConfigPushType;
168
+ payload: string;
169
+ status: ConfigPushStatus;
170
+ createdBy: string;
171
+ createdAt: number;
172
+ }
173
+
174
+ // Options for creating a mock config push
175
+ export interface CreateMockConfigPushOptions {
176
+ relayId?: string;
177
+ pushType?: ConfigPushType;
178
+ payload?: string;
179
+ status?: ConfigPushStatus;
180
+ createdBy?: string;
181
+ }
182
+
183
+ /**
184
+ * Create a mock config push in the test database.
185
+ */
186
+ export async function createMockConfigPush(
187
+ t: ReturnType<typeof convexTest>,
188
+ options: CreateMockConfigPushOptions = {}
189
+ ): Promise<MockConfigPush> {
190
+ const now = Date.now();
191
+ const relayId = options.relayId ?? `relay-${now}`;
192
+ const pushType = options.pushType ?? "credential";
193
+ const payload = options.payload ?? JSON.stringify({ test: "data" });
194
+ const status = options.status ?? "pending";
195
+ const createdBy = options.createdBy ?? "test-user";
196
+
197
+ const pushId = await t.run(async (ctx) => {
198
+ return await ctx.db.insert("configPushQueue", {
199
+ relayId,
200
+ pushType,
201
+ payload,
202
+ status,
203
+ createdBy,
204
+ createdAt: now,
205
+ });
206
+ });
207
+
208
+ return {
209
+ _id: pushId as Id<"configPushQueue">,
210
+ relayId,
211
+ pushType,
212
+ payload,
213
+ status,
214
+ createdBy,
215
+ createdAt: now,
216
+ };
217
+ }
218
+
219
+ // Mock relay status data structure
220
+ export interface MockRelayStatus {
221
+ _id: Id<"relayStatus">;
222
+ relayId: string;
223
+ capabilities: Capability[];
224
+ version?: string;
225
+ hostname?: string;
226
+ platform?: string;
227
+ lastHeartbeatAt: number;
228
+ createdAt: number;
229
+ updatedAt: number;
230
+ }
231
+
232
+ // Options for creating a mock relay status
233
+ export interface CreateMockRelayStatusOptions {
234
+ relayId?: string;
235
+ capabilities?: Capability[];
236
+ version?: string;
237
+ hostname?: string;
238
+ platform?: string;
239
+ lastHeartbeatAt?: number;
240
+ }
241
+
242
+ /**
243
+ * Create a mock relay status in the test database.
244
+ */
245
+ export async function createMockRelayStatus(
246
+ t: ReturnType<typeof convexTest>,
247
+ options: CreateMockRelayStatusOptions = {}
248
+ ): Promise<MockRelayStatus> {
249
+ const now = Date.now();
250
+ const relayId = options.relayId ?? `relay-${now}`;
251
+ const capabilities = options.capabilities ?? ["local_cmd"];
252
+ const lastHeartbeatAt = options.lastHeartbeatAt ?? now;
253
+
254
+ const statusId = await t.run(async (ctx) => {
255
+ return await ctx.db.insert("relayStatus", {
256
+ relayId,
257
+ capabilities,
258
+ version: options.version,
259
+ hostname: options.hostname,
260
+ platform: options.platform,
261
+ lastHeartbeatAt,
262
+ createdAt: now,
263
+ updatedAt: now,
264
+ });
265
+ });
266
+
267
+ return {
268
+ _id: statusId as Id<"relayStatus">,
269
+ relayId,
270
+ capabilities,
271
+ version: options.version,
272
+ hostname: options.hostname,
273
+ platform: options.platform,
274
+ lastHeartbeatAt,
275
+ createdAt: now,
276
+ updatedAt: now,
277
+ };
278
+ }
279
+
280
+ // Mock credential inventory data structure
281
+ export interface MockCredentialInventory {
282
+ _id: Id<"relayCredentialInventory">;
283
+ relayId: string;
284
+ credentialName: string;
285
+ credentialType: CredentialType;
286
+ targetHost?: string;
287
+ storageMode: StorageMode;
288
+ lastUpdatedAt: number;
289
+ reportedAt: number;
290
+ }
291
+
292
+ // Options for creating a mock credential inventory
293
+ export interface CreateMockCredentialInventoryOptions {
294
+ relayId?: string;
295
+ credentialName?: string;
296
+ credentialType?: CredentialType;
297
+ targetHost?: string;
298
+ storageMode?: StorageMode;
299
+ }
300
+
301
+ /**
302
+ * Create a mock credential inventory entry in the test database.
303
+ */
304
+ export async function createMockCredentialInventory(
305
+ t: ReturnType<typeof convexTest>,
306
+ options: CreateMockCredentialInventoryOptions = {}
307
+ ): Promise<MockCredentialInventory> {
308
+ const now = Date.now();
309
+ const relayId = options.relayId ?? `relay-${now}`;
310
+ const credentialName = options.credentialName ?? `cred-${now}`;
311
+ const credentialType = options.credentialType ?? "ssh_key";
312
+ const storageMode = options.storageMode ?? "relay_only";
313
+
314
+ const credId = await t.run(async (ctx) => {
315
+ return await ctx.db.insert("relayCredentialInventory", {
316
+ relayId,
317
+ credentialName,
318
+ credentialType,
319
+ targetHost: options.targetHost,
320
+ storageMode,
321
+ lastUpdatedAt: now,
322
+ reportedAt: now,
323
+ });
324
+ });
325
+
326
+ return {
327
+ _id: credId as Id<"relayCredentialInventory">,
328
+ relayId,
329
+ credentialName,
330
+ credentialType,
331
+ targetHost: options.targetHost,
332
+ storageMode,
333
+ lastUpdatedAt: now,
334
+ reportedAt: now,
335
+ };
336
+ }
337
+
338
+ // Mock shared credential data structure
339
+ export interface MockSharedCredential {
340
+ _id: Id<"sharedCredentials">;
341
+ name: string;
342
+ credentialType: CredentialType;
343
+ targetHost?: string;
344
+ encryptedValue: string;
345
+ assignedRelays: string[];
346
+ createdBy: string;
347
+ createdAt: number;
348
+ updatedAt: number;
349
+ }
350
+
351
+ // Options for creating a mock shared credential
352
+ export interface CreateMockSharedCredentialOptions {
353
+ name?: string;
354
+ credentialType?: CredentialType;
355
+ targetHost?: string;
356
+ encryptedValue?: string;
357
+ assignedRelays?: string[];
358
+ createdBy?: string;
359
+ }
360
+
361
+ /**
362
+ * Create a mock shared credential in the test database.
363
+ */
364
+ export async function createMockSharedCredential(
365
+ t: ReturnType<typeof convexTest>,
366
+ options: CreateMockSharedCredentialOptions = {}
367
+ ): Promise<MockSharedCredential> {
368
+ const now = Date.now();
369
+ const name = options.name ?? `shared-cred-${now}`;
370
+ const credentialType = options.credentialType ?? "ssh_key";
371
+ const encryptedValue = options.encryptedValue ?? "encrypted-test-value";
372
+ const assignedRelays = options.assignedRelays ?? [];
373
+ const createdBy = options.createdBy ?? "test-user";
374
+
375
+ const credId = await t.run(async (ctx) => {
376
+ return await ctx.db.insert("sharedCredentials", {
377
+ name,
378
+ credentialType,
379
+ targetHost: options.targetHost,
380
+ encryptedValue,
381
+ assignedRelays,
382
+ createdBy,
383
+ createdAt: now,
384
+ updatedAt: now,
385
+ });
386
+ });
387
+
388
+ return {
389
+ _id: credId as Id<"sharedCredentials">,
390
+ name,
391
+ credentialType,
392
+ targetHost: options.targetHost,
393
+ encryptedValue,
394
+ assignedRelays,
395
+ createdBy,
396
+ createdAt: now,
397
+ updatedAt: now,
398
+ };
399
+ }
@@ -0,0 +1,256 @@
1
+ import { convexTest } from "convex-test";
2
+ import schema from "./schema";
3
+ import type { Id } from "./_generated/dataModel";
4
+
5
+ // Import modules for convex-test
6
+ const modules: Record<string, () => Promise<unknown>> = import.meta.glob(
7
+ "./**/*.ts"
8
+ );
9
+
10
+ /**
11
+ * Create a test Convex instance for remoteCmdRelay component.
12
+ */
13
+ export function createRelayTestConvex() {
14
+ return convexTest(schema, modules);
15
+ }
16
+
17
+ export type RelayTestConvex = ReturnType<typeof createRelayTestConvex>;
18
+
19
+ // ===== Test Data Helpers =====
20
+
21
+ export interface CreateRelayAssignmentOptions {
22
+ apiKeyId?: string;
23
+ machineId?: string;
24
+ name?: string;
25
+ enabled?: boolean;
26
+ createdBy?: string;
27
+ }
28
+
29
+ /**
30
+ * Create a relay assignment in the test database
31
+ */
32
+ export async function createTestRelayAssignment(
33
+ t: RelayTestConvex,
34
+ options: CreateRelayAssignmentOptions = {}
35
+ ): Promise<{
36
+ _id: Id<"relayAssignments">;
37
+ apiKeyId: string;
38
+ machineId: string;
39
+ name: string;
40
+ enabled: boolean;
41
+ createdBy: string;
42
+ }> {
43
+ const now = Date.now();
44
+ const apiKeyId = options.apiKeyId ?? `test-api-key-${now}`;
45
+ const machineId = options.machineId ?? `test-machine-${now}`;
46
+ const name = options.name ?? "Test Relay";
47
+ const enabled = options.enabled ?? true;
48
+ const createdBy = options.createdBy ?? "test-user";
49
+
50
+ const id = await t.run(async (ctx) => {
51
+ return await ctx.db.insert("relayAssignments", {
52
+ apiKeyId,
53
+ machineId,
54
+ name,
55
+ enabled,
56
+ createdBy,
57
+ createdAt: now,
58
+ updatedAt: now,
59
+ });
60
+ });
61
+
62
+ return {
63
+ _id: id,
64
+ apiKeyId,
65
+ machineId,
66
+ name,
67
+ enabled,
68
+ createdBy,
69
+ };
70
+ }
71
+
72
+ export interface CreateCommandOptions {
73
+ machineId: string;
74
+ command?: string;
75
+ targetType?: "local" | "ssh";
76
+ targetHost?: string;
77
+ targetPort?: number;
78
+ targetUsername?: string;
79
+ timeoutMs?: number;
80
+ status?: "pending" | "claimed" | "executing" | "completed" | "failed" | "timeout";
81
+ createdBy?: string;
82
+ }
83
+
84
+ /**
85
+ * Create a command in the test database
86
+ */
87
+ export async function createTestCommand(
88
+ t: RelayTestConvex,
89
+ options: CreateCommandOptions
90
+ ): Promise<{
91
+ _id: Id<"commandQueue">;
92
+ machineId: string;
93
+ command: string;
94
+ targetType: "local" | "ssh";
95
+ status: "pending" | "claimed" | "executing" | "completed" | "failed" | "timeout";
96
+ }> {
97
+ const now = Date.now();
98
+ const command = options.command ?? "echo test";
99
+ const targetType = options.targetType ?? "local";
100
+ const status = options.status ?? "pending";
101
+ const createdBy = options.createdBy ?? "test-user";
102
+
103
+ const id = await t.run(async (ctx) => {
104
+ return await ctx.db.insert("commandQueue", {
105
+ machineId: options.machineId,
106
+ command,
107
+ targetType,
108
+ targetHost: options.targetHost,
109
+ targetPort: options.targetPort ?? 22,
110
+ targetUsername: options.targetUsername,
111
+ timeoutMs: options.timeoutMs ?? 30000,
112
+ status,
113
+ createdBy,
114
+ createdAt: now,
115
+ updatedAt: now,
116
+ });
117
+ });
118
+
119
+ return {
120
+ _id: id,
121
+ machineId: options.machineId,
122
+ command,
123
+ targetType,
124
+ status,
125
+ };
126
+ }
127
+
128
+ export interface CreateRelayStatusOptions {
129
+ relayId: string;
130
+ capabilities?: ("ssh" | "local_cmd" | "perf_metrics")[];
131
+ metrics?: {
132
+ cpuPercent?: number;
133
+ memoryPercent?: number;
134
+ memoryUsedMb?: number;
135
+ memoryTotalMb?: number;
136
+ };
137
+ version?: string;
138
+ hostname?: string;
139
+ platform?: string;
140
+ }
141
+
142
+ /**
143
+ * Create a relay status record in the test database
144
+ */
145
+ export async function createTestRelayStatus(
146
+ t: RelayTestConvex,
147
+ options: CreateRelayStatusOptions
148
+ ): Promise<Id<"relayStatus">> {
149
+ const now = Date.now();
150
+ const capabilities = options.capabilities ?? ["local_cmd"];
151
+
152
+ return await t.run(async (ctx) => {
153
+ return await ctx.db.insert("relayStatus", {
154
+ relayId: options.relayId,
155
+ capabilities,
156
+ metrics: options.metrics,
157
+ version: options.version ?? "1.0.0",
158
+ hostname: options.hostname ?? "test-host",
159
+ platform: options.platform ?? "linux-x64",
160
+ lastHeartbeatAt: now,
161
+ createdAt: now,
162
+ updatedAt: now,
163
+ });
164
+ });
165
+ }
166
+
167
+ export interface CreateCredentialInventoryOptions {
168
+ relayId: string;
169
+ credentialName?: string;
170
+ credentialType?: "ssh_key" | "password" | "api_key";
171
+ targetHost?: string;
172
+ storageMode?: "relay_only" | "shared";
173
+ }
174
+
175
+ /**
176
+ * Create a credential inventory record in the test database
177
+ */
178
+ export async function createTestCredentialInventory(
179
+ t: RelayTestConvex,
180
+ options: CreateCredentialInventoryOptions
181
+ ): Promise<Id<"relayCredentialInventory">> {
182
+ const now = Date.now();
183
+
184
+ return await t.run(async (ctx) => {
185
+ return await ctx.db.insert("relayCredentialInventory", {
186
+ relayId: options.relayId,
187
+ credentialName: options.credentialName ?? `cred-${now}`,
188
+ credentialType: options.credentialType ?? "ssh_key",
189
+ targetHost: options.targetHost,
190
+ storageMode: options.storageMode ?? "relay_only",
191
+ lastUpdatedAt: now,
192
+ reportedAt: now,
193
+ });
194
+ });
195
+ }
196
+
197
+ export interface CreateSharedCredentialOptions {
198
+ name?: string;
199
+ credentialType?: "ssh_key" | "password" | "api_key";
200
+ targetHost?: string;
201
+ encryptedValue?: string;
202
+ assignedRelays?: string[];
203
+ createdBy?: string;
204
+ }
205
+
206
+ /**
207
+ * Create a shared credential in the test database
208
+ */
209
+ export async function createTestSharedCredential(
210
+ t: RelayTestConvex,
211
+ options: CreateSharedCredentialOptions = {}
212
+ ): Promise<Id<"sharedCredentials">> {
213
+ const now = Date.now();
214
+
215
+ return await t.run(async (ctx) => {
216
+ return await ctx.db.insert("sharedCredentials", {
217
+ name: options.name ?? `shared-cred-${now}`,
218
+ credentialType: options.credentialType ?? "ssh_key",
219
+ targetHost: options.targetHost,
220
+ encryptedValue: options.encryptedValue ?? "encrypted-value",
221
+ assignedRelays: options.assignedRelays ?? [],
222
+ createdBy: options.createdBy ?? "test-user",
223
+ createdAt: now,
224
+ updatedAt: now,
225
+ });
226
+ });
227
+ }
228
+
229
+ export interface CreateConfigPushOptions {
230
+ relayId: string;
231
+ pushType?: "credential" | "ssh_targets" | "allowed_commands" | "metrics_interval";
232
+ payload?: string;
233
+ status?: "pending" | "sent" | "acked" | "failed";
234
+ createdBy?: string;
235
+ }
236
+
237
+ /**
238
+ * Create a config push in the test database
239
+ */
240
+ export async function createTestConfigPush(
241
+ t: RelayTestConvex,
242
+ options: CreateConfigPushOptions
243
+ ): Promise<Id<"configPushQueue">> {
244
+ const now = Date.now();
245
+
246
+ return await t.run(async (ctx) => {
247
+ return await ctx.db.insert("configPushQueue", {
248
+ relayId: options.relayId,
249
+ pushType: options.pushType ?? "credential",
250
+ payload: options.payload ?? JSON.stringify({ test: true }),
251
+ status: options.status ?? "pending",
252
+ createdBy: options.createdBy ?? "test-user",
253
+ createdAt: now,
254
+ });
255
+ });
256
+ }