@tinyrack/devsync 1.0.0 → 1.1.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,179 @@
1
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+
6
+ import { syncSecretArtifactSuffix } from "#app/config/sync.ts";
7
+ import { addSyncTarget } from "#app/services/add.ts";
8
+ import { initializeSync } from "#app/services/init.ts";
9
+ import { pullSync } from "#app/services/pull.ts";
10
+ import { pushSync } from "#app/services/push.ts";
11
+ import { createSyncContext } from "#app/services/runtime.ts";
12
+ import { setSyncTargetMode } from "#app/services/set.ts";
13
+ import {
14
+ createAgeKeyPair,
15
+ createTemporaryDirectory,
16
+ writeIdentityFile,
17
+ } from "../test/helpers/sync-fixture.ts";
18
+
19
+ const temporaryDirectories: string[] = [];
20
+
21
+ const createWorkspace = async () => {
22
+ const directory = await createTemporaryDirectory("devsync-dry-run-");
23
+
24
+ temporaryDirectories.push(directory);
25
+
26
+ return directory;
27
+ };
28
+
29
+ const createEnvironment = (
30
+ homeDirectory: string,
31
+ xdgConfigHome: string,
32
+ ): NodeJS.ProcessEnv => {
33
+ return {
34
+ HOME: homeDirectory,
35
+ XDG_CONFIG_HOME: xdgConfigHome,
36
+ };
37
+ };
38
+
39
+ afterEach(async () => {
40
+ while (temporaryDirectories.length > 0) {
41
+ const directory = temporaryDirectories.pop();
42
+
43
+ if (directory !== undefined) {
44
+ await rm(directory, { force: true, recursive: true });
45
+ }
46
+ }
47
+ });
48
+
49
+ describe("sync dry runs", () => {
50
+ it("reports push changes without mutating repository artifacts", async () => {
51
+ const workspace = await createWorkspace();
52
+ const homeDirectory = join(workspace, "home");
53
+ const xdgConfigHome = join(workspace, "xdg");
54
+ const bundleDirectory = join(homeDirectory, "bundle");
55
+ const plainFile = join(bundleDirectory, "plain.txt");
56
+ const secretFile = join(bundleDirectory, "token.txt");
57
+ const ageKeys = await createAgeKeyPair();
58
+ const context = createSyncContext({
59
+ environment: createEnvironment(homeDirectory, xdgConfigHome),
60
+ });
61
+
62
+ await writeIdentityFile(xdgConfigHome, ageKeys.identity);
63
+ await mkdir(bundleDirectory, { recursive: true });
64
+ await writeFile(plainFile, "plain\n", "utf8");
65
+ await writeFile(secretFile, "secret\n", "utf8");
66
+
67
+ await initializeSync(
68
+ {
69
+ identityFile: "$XDG_CONFIG_HOME/devsync/age/keys.txt",
70
+ recipients: [ageKeys.recipient],
71
+ },
72
+ context,
73
+ );
74
+ await addSyncTarget(
75
+ {
76
+ secret: false,
77
+ target: bundleDirectory,
78
+ },
79
+ context,
80
+ );
81
+ await setSyncTargetMode(
82
+ {
83
+ recursive: false,
84
+ state: "secret",
85
+ target: secretFile,
86
+ },
87
+ context,
88
+ );
89
+
90
+ const result = await pushSync(
91
+ {
92
+ dryRun: true,
93
+ },
94
+ context,
95
+ );
96
+
97
+ expect(result.dryRun).toBe(true);
98
+ expect(result.directoryCount).toBe(1);
99
+ expect(result.plainFileCount).toBe(1);
100
+ expect(result.encryptedFileCount).toBe(1);
101
+ await expect(
102
+ readFile(
103
+ join(xdgConfigHome, "devsync", "sync", "files", "bundle", "plain.txt"),
104
+ "utf8",
105
+ ),
106
+ ).rejects.toMatchObject({
107
+ code: "ENOENT",
108
+ });
109
+ await expect(
110
+ readFile(
111
+ join(
112
+ xdgConfigHome,
113
+ "devsync",
114
+ "sync",
115
+ "files",
116
+ "bundle",
117
+ `token.txt${syncSecretArtifactSuffix}`,
118
+ ),
119
+ "utf8",
120
+ ),
121
+ ).rejects.toMatchObject({
122
+ code: "ENOENT",
123
+ });
124
+ });
125
+
126
+ it("reports pull changes without mutating local files", async () => {
127
+ const workspace = await createWorkspace();
128
+ const homeDirectory = join(workspace, "home");
129
+ const xdgConfigHome = join(workspace, "xdg");
130
+ const bundleDirectory = join(homeDirectory, "bundle");
131
+ const plainFile = join(bundleDirectory, "plain.txt");
132
+ const extraFile = join(bundleDirectory, "extra.txt");
133
+ const ageKeys = await createAgeKeyPair();
134
+ const context = createSyncContext({
135
+ environment: createEnvironment(homeDirectory, xdgConfigHome),
136
+ });
137
+
138
+ await writeIdentityFile(xdgConfigHome, ageKeys.identity);
139
+ await mkdir(bundleDirectory, { recursive: true });
140
+ await writeFile(plainFile, "plain\n", "utf8");
141
+
142
+ await initializeSync(
143
+ {
144
+ identityFile: "$XDG_CONFIG_HOME/devsync/age/keys.txt",
145
+ recipients: [ageKeys.recipient],
146
+ },
147
+ context,
148
+ );
149
+ await addSyncTarget(
150
+ {
151
+ secret: false,
152
+ target: bundleDirectory,
153
+ },
154
+ context,
155
+ );
156
+ await pushSync(
157
+ {
158
+ dryRun: false,
159
+ },
160
+ context,
161
+ );
162
+
163
+ await writeFile(plainFile, "changed locally\n", "utf8");
164
+ await writeFile(extraFile, "leave me\n", "utf8");
165
+
166
+ const result = await pullSync(
167
+ {
168
+ dryRun: true,
169
+ },
170
+ context,
171
+ );
172
+
173
+ expect(result.dryRun).toBe(true);
174
+ expect(result.plainFileCount).toBe(1);
175
+ expect(result.deletedLocalCount).toBeGreaterThanOrEqual(1);
176
+ expect(await readFile(plainFile, "utf8")).toBe("changed locally\n");
177
+ expect(await readFile(extraFile, "utf8")).toBe("leave me\n");
178
+ });
179
+ });