apify-test-tools 0.10.1-beta.2 → 0.10.1-beta.4

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.
Files changed (147) hide show
  1. package/.github/actions/checkout-restore-dependencies/action.yaml +4 -3
  2. package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +7 -2
  3. package/.github/workflows/public_platform-tests.yaml +0 -1
  4. package/.github/workflows-package-version +1 -1
  5. package/CHANGELOG.md +6 -0
  6. package/CONTRIBUTING.md +13 -3
  7. package/README.md +2 -2
  8. package/bin/build-from-local.ts +8 -7
  9. package/bin/build.ts +22 -21
  10. package/bin/diff-changes.ts +11 -10
  11. package/bin/diff-json-schema.ts +14 -12
  12. package/bin/git.ts +89 -47
  13. package/bin/logger.ts +23 -0
  14. package/bin/main.ts +44 -38
  15. package/bin/output.ts +6 -0
  16. package/bin/slack.ts +8 -7
  17. package/bin/test-report.ts +22 -21
  18. package/bin/types.ts +0 -1
  19. package/bin/utils/config/actor-json.ts +84 -0
  20. package/bin/utils/config/load-config.ts +46 -46
  21. package/bin/utils/config/parser.ts +33 -28
  22. package/bin/utils/config/structures/base.ts +7 -0
  23. package/bin/utils/json-file.ts +11 -14
  24. package/bin/utils/path/repo-relative.ts +156 -0
  25. package/bin/utils/path/schema.ts +18 -0
  26. package/bin/utils.ts +63 -44
  27. package/dist/bin/build-from-local.d.ts +1 -1
  28. package/dist/bin/build-from-local.d.ts.map +1 -1
  29. package/dist/bin/build-from-local.js +8 -7
  30. package/dist/bin/build-from-local.js.map +1 -1
  31. package/dist/bin/build.d.ts.map +1 -1
  32. package/dist/bin/build.js +22 -21
  33. package/dist/bin/build.js.map +1 -1
  34. package/dist/bin/diff-changes.d.ts +1 -1
  35. package/dist/bin/diff-changes.d.ts.map +1 -1
  36. package/dist/bin/diff-changes.js +9 -8
  37. package/dist/bin/diff-changes.js.map +1 -1
  38. package/dist/bin/diff-json-schema.d.ts +1 -1
  39. package/dist/bin/diff-json-schema.d.ts.map +1 -1
  40. package/dist/bin/diff-json-schema.js +13 -12
  41. package/dist/bin/diff-json-schema.js.map +1 -1
  42. package/dist/bin/git.d.ts +10 -10
  43. package/dist/bin/git.d.ts.map +1 -1
  44. package/dist/bin/git.js +78 -37
  45. package/dist/bin/git.js.map +1 -1
  46. package/dist/bin/logger.d.ts +11 -0
  47. package/dist/bin/logger.d.ts.map +1 -0
  48. package/dist/bin/logger.js +20 -0
  49. package/dist/bin/logger.js.map +1 -0
  50. package/dist/bin/main.d.ts +1 -1
  51. package/dist/bin/main.js +44 -38
  52. package/dist/bin/main.js.map +1 -1
  53. package/dist/bin/output.d.ts +3 -0
  54. package/dist/bin/output.d.ts.map +1 -0
  55. package/dist/bin/output.js +6 -0
  56. package/dist/bin/output.js.map +1 -0
  57. package/dist/bin/slack.d.ts.map +1 -1
  58. package/dist/bin/slack.js +8 -7
  59. package/dist/bin/slack.js.map +1 -1
  60. package/dist/bin/test-report.d.ts.map +1 -1
  61. package/dist/bin/test-report.js +22 -21
  62. package/dist/bin/test-report.js.map +1 -1
  63. package/dist/bin/types.d.ts +0 -1
  64. package/dist/bin/types.d.ts.map +1 -1
  65. package/dist/bin/utils/config/actor-json.d.ts +14 -0
  66. package/dist/bin/utils/config/actor-json.d.ts.map +1 -0
  67. package/dist/bin/utils/config/actor-json.js +60 -0
  68. package/dist/bin/utils/config/actor-json.js.map +1 -0
  69. package/dist/bin/utils/config/load-config.d.ts +12 -2
  70. package/dist/bin/utils/config/load-config.d.ts.map +1 -1
  71. package/dist/bin/utils/config/load-config.js +32 -31
  72. package/dist/bin/utils/config/load-config.js.map +1 -1
  73. package/dist/bin/utils/config/parser.d.ts +4 -12
  74. package/dist/bin/utils/config/parser.d.ts.map +1 -1
  75. package/dist/bin/utils/config/parser.js +24 -23
  76. package/dist/bin/utils/config/parser.js.map +1 -1
  77. package/dist/bin/utils/config/structures/base.d.ts +5 -0
  78. package/dist/bin/utils/config/structures/base.d.ts.map +1 -1
  79. package/dist/bin/utils/config/structures/base.js.map +1 -1
  80. package/dist/bin/utils/json-file.d.ts +3 -2
  81. package/dist/bin/utils/json-file.d.ts.map +1 -1
  82. package/dist/bin/utils/json-file.js +8 -13
  83. package/dist/bin/utils/json-file.js.map +1 -1
  84. package/dist/bin/utils/path/repo-relative.d.ts +35 -0
  85. package/dist/bin/utils/path/repo-relative.d.ts.map +1 -0
  86. package/dist/bin/utils/path/repo-relative.js +137 -0
  87. package/dist/bin/utils/path/repo-relative.js.map +1 -0
  88. package/dist/bin/utils/path/schema.d.ts +7 -0
  89. package/dist/bin/utils/path/schema.d.ts.map +1 -0
  90. package/dist/bin/utils/path/schema.js +16 -0
  91. package/dist/bin/utils/path/schema.js.map +1 -0
  92. package/dist/bin/utils.d.ts +14 -6
  93. package/dist/bin/utils.d.ts.map +1 -1
  94. package/dist/bin/utils.js +52 -36
  95. package/dist/bin/utils.js.map +1 -1
  96. package/dist/test/unit/bin/build-from-local.test.js +4 -40
  97. package/dist/test/unit/bin/build-from-local.test.js.map +1 -1
  98. package/dist/test/unit/bin/diff-changes.test.js +97 -96
  99. package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
  100. package/dist/test/unit/bin/diff.test.js +52 -48
  101. package/dist/test/unit/bin/diff.test.js.map +1 -1
  102. package/dist/test/unit/bin/git-command.test.d.ts +2 -0
  103. package/dist/test/unit/bin/git-command.test.d.ts.map +1 -0
  104. package/dist/test/unit/bin/git-command.test.js +54 -0
  105. package/dist/test/unit/bin/git-command.test.js.map +1 -0
  106. package/dist/test/unit/bin/git.test.js +211 -94
  107. package/dist/test/unit/bin/git.test.js.map +1 -1
  108. package/dist/test/unit/bin/logger.test.d.ts +2 -0
  109. package/dist/test/unit/bin/logger.test.d.ts.map +1 -0
  110. package/dist/test/unit/bin/logger.test.js +44 -0
  111. package/dist/test/unit/bin/logger.test.js.map +1 -0
  112. package/dist/test/unit/bin/logging.test.d.ts +2 -0
  113. package/dist/test/unit/bin/logging.test.d.ts.map +1 -0
  114. package/dist/test/unit/bin/logging.test.js +43 -0
  115. package/dist/test/unit/bin/logging.test.js.map +1 -0
  116. package/dist/test/unit/bin/utils/config/load-config.test.js +47 -25
  117. package/dist/test/unit/bin/utils/config/load-config.test.js.map +1 -1
  118. package/dist/test/unit/bin/utils/config/parser.test.js +64 -19
  119. package/dist/test/unit/bin/utils/config/parser.test.js.map +1 -1
  120. package/dist/test/unit/bin/utils/files.test.js +16 -27
  121. package/dist/test/unit/bin/utils/files.test.js.map +1 -1
  122. package/dist/test/unit/bin/utils/path/repo-relative.test.d.ts +2 -0
  123. package/dist/test/unit/bin/utils/path/repo-relative.test.d.ts.map +1 -0
  124. package/dist/test/unit/bin/utils/path/repo-relative.test.js +196 -0
  125. package/dist/test/unit/bin/utils/path/repo-relative.test.js.map +1 -0
  126. package/dist/test/unit/bin/utils/path/schema.test.d.ts +2 -0
  127. package/dist/test/unit/bin/utils/path/schema.test.d.ts.map +1 -0
  128. package/dist/test/unit/bin/utils/path/schema.test.js +52 -0
  129. package/dist/test/unit/bin/utils/path/schema.test.js.map +1 -0
  130. package/dist/test/unit/should-built-and-test.test.js +40 -38
  131. package/dist/test/unit/should-built-and-test.test.js.map +1 -1
  132. package/dist/tsconfig.tsbuildinfo +1 -1
  133. package/eslint.config.mjs +9 -1
  134. package/package.json +5 -5
  135. package/test/unit/bin/build-from-local.test.ts +4 -54
  136. package/test/unit/bin/diff-changes.test.ts +97 -96
  137. package/test/unit/bin/diff.test.ts +53 -48
  138. package/test/unit/bin/git-command.test.ts +71 -0
  139. package/test/unit/bin/git.test.ts +246 -91
  140. package/test/unit/bin/logger.test.ts +55 -0
  141. package/test/unit/bin/logging.test.ts +52 -0
  142. package/test/unit/bin/utils/config/load-config.test.ts +63 -26
  143. package/test/unit/bin/utils/config/parser.test.ts +81 -27
  144. package/test/unit/bin/utils/files.test.ts +19 -33
  145. package/test/unit/bin/utils/path/repo-relative.test.ts +250 -0
  146. package/test/unit/bin/utils/path/schema.test.ts +65 -0
  147. package/test/unit/should-built-and-test.test.ts +40 -38
@@ -13,83 +13,83 @@ describe('isCosmeticOnlyJsonSchemaChange', () => {
13
13
  let gitCommandSpy: MockInstance;
14
14
 
15
15
  beforeEach(() => {
16
- gitCommandSpy = vi.spyOn(Utils, 'spawnCommandInGhWorkspace');
16
+ gitCommandSpy = vi.spyOn(Utils, 'runGitCommand');
17
17
  });
18
18
 
19
19
  const mockGitCalls = (oldJson: string, newJson: string) => {
20
- gitCommandSpy.mockReturnValueOnce(oldJson).mockReturnValueOnce(newJson);
20
+ gitCommandSpy.mockResolvedValueOnce(oldJson).mockResolvedValueOnce(newJson);
21
21
  };
22
22
 
23
- test('returns true when nothing changed', () => {
23
+ test('returns true when nothing changed', async () => {
24
24
  const json = JSON.stringify({ type: 'string', title: 'My field' });
25
25
  mockGitCalls(json, json);
26
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
26
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
27
27
  });
28
28
 
29
- test('returns true when only title changes', () => {
29
+ test('returns true when only title changes', async () => {
30
30
  mockGitCalls(
31
31
  JSON.stringify({ type: 'string', title: 'Old title' }),
32
32
  JSON.stringify({ type: 'string', title: 'New title' }),
33
33
  );
34
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
34
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
35
35
  });
36
36
 
37
- test('returns true when only description changes', () => {
37
+ test('returns true when only description changes', async () => {
38
38
  mockGitCalls(
39
39
  JSON.stringify({ type: 'string', description: 'Old desc' }),
40
40
  JSON.stringify({ type: 'string', description: 'New desc' }),
41
41
  );
42
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
42
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
43
43
  });
44
44
 
45
- test('returns true when only example changes', () => {
45
+ test('returns true when only example changes', async () => {
46
46
  mockGitCalls(
47
47
  JSON.stringify({ type: 'string', example: 'foo' }),
48
48
  JSON.stringify({ type: 'string', example: 'bar' }),
49
49
  );
50
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
50
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
51
51
  });
52
52
 
53
- test('returns true when only enumTitles changes', () => {
53
+ test('returns true when only enumTitles changes', async () => {
54
54
  mockGitCalls(
55
55
  JSON.stringify({ type: 'string', enum: ['a', 'b'], enumTitles: ['Option A', 'Option B'] }),
56
56
  JSON.stringify({ type: 'string', enum: ['a', 'b'], enumTitles: ['Choice A', 'Choice B'] }),
57
57
  );
58
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
58
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
59
59
  });
60
60
 
61
- test('returns true when only sectionCaption changes', () => {
61
+ test('returns true when only sectionCaption changes', async () => {
62
62
  mockGitCalls(
63
63
  JSON.stringify({ sectionCaption: 'Old caption' }),
64
64
  JSON.stringify({ sectionCaption: 'New caption' }),
65
65
  );
66
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
66
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
67
67
  });
68
68
 
69
- test('returns true when only sectionDescription changes', () => {
69
+ test('returns true when only sectionDescription changes', async () => {
70
70
  mockGitCalls(JSON.stringify({ sectionDescription: 'Old' }), JSON.stringify({ sectionDescription: 'New' }));
71
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
71
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
72
72
  });
73
73
 
74
- test('returns false when a functional field (type) changes', () => {
74
+ test('returns false when a functional field (type) changes', async () => {
75
75
  mockGitCalls(
76
76
  JSON.stringify({ type: 'string', title: 'My field' }),
77
77
  JSON.stringify({ type: 'integer', title: 'My field' }),
78
78
  );
79
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false);
79
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
80
80
  });
81
81
 
82
- test('returns false when a functional field is added', () => {
82
+ test('returns false when a functional field is added', async () => {
83
83
  mockGitCalls(JSON.stringify({ title: 'My field' }), JSON.stringify({ title: 'My field', type: 'string' }));
84
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false);
84
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
85
85
  });
86
86
 
87
- test('returns false when a functional field is removed', () => {
87
+ test('returns false when a functional field is removed', async () => {
88
88
  mockGitCalls(JSON.stringify({ type: 'string', title: 'My field' }), JSON.stringify({ title: 'My field' }));
89
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false);
89
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
90
90
  });
91
91
 
92
- test('returns true when non-functional fields change at nested level (input schema properties)', () => {
92
+ test('returns true when non-functional fields change at nested level (input schema properties)', async () => {
93
93
  mockGitCalls(
94
94
  JSON.stringify({
95
95
  type: 'object',
@@ -104,10 +104,10 @@ describe('isCosmeticOnlyJsonSchemaChange', () => {
104
104
  },
105
105
  }),
106
106
  );
107
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
107
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
108
108
  });
109
109
 
110
- test('returns false when a functional nested field changes alongside non-functional ones', () => {
110
+ test('returns false when a functional nested field changes alongside non-functional ones', async () => {
111
111
  mockGitCalls(
112
112
  JSON.stringify({
113
113
  type: 'object',
@@ -118,49 +118,54 @@ describe('isCosmeticOnlyJsonSchemaChange', () => {
118
118
  properties: { myField: { type: 'integer', title: 'New title' } },
119
119
  }),
120
120
  );
121
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false);
121
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
122
122
  });
123
123
 
124
- test('returns false when a new property is added to input schema', () => {
124
+ test('returns false when a new property is added to input schema', async () => {
125
125
  mockGitCalls(
126
126
  JSON.stringify({ properties: { fieldA: { type: 'string' } } }),
127
127
  JSON.stringify({ properties: { fieldA: { type: 'string' }, fieldB: { type: 'integer' } } }),
128
128
  );
129
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false);
129
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
130
130
  });
131
131
 
132
- test('returns false when a property is removed from input schema', () => {
132
+ test('returns false when a property is removed from input schema', async () => {
133
133
  mockGitCalls(
134
134
  JSON.stringify({ properties: { fieldA: { type: 'string' }, fieldB: { type: 'integer' } } }),
135
135
  JSON.stringify({ properties: { fieldA: { type: 'string' } } }),
136
136
  );
137
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false);
137
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
138
138
  });
139
139
 
140
- test('returns false when invalid JSON in old content', () => {
140
+ test('returns false when invalid JSON in old content', async () => {
141
141
  mockGitCalls('not json', '{}');
142
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false);
142
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
143
143
  });
144
144
 
145
- test('returns false when invalid JSON in new content', () => {
145
+ test('returns false when invalid JSON in new content', async () => {
146
146
  mockGitCalls('{}', 'not json');
147
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false);
147
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
148
148
  });
149
149
 
150
- test('returns false when array content changes (not under a non-functional key)', () => {
150
+ test('returns false when git cannot read a schema version', async () => {
151
+ gitCommandSpy.mockRejectedValueOnce(new Error('missing schema'));
152
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
153
+ });
154
+
155
+ test('returns false when array content changes (not under a non-functional key)', async () => {
151
156
  mockGitCalls(JSON.stringify({ enum: [1, 2, 3] }), JSON.stringify({ enum: [1, 2, 4] }));
152
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false);
157
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
153
158
  });
154
159
 
155
- test('returns true when example array changes (non-functional key)', () => {
160
+ test('returns true when example array changes (non-functional key)', async () => {
156
161
  mockGitCalls(
157
162
  JSON.stringify({ type: 'array', example: [1, 2, 3] }),
158
163
  JSON.stringify({ type: 'array', example: [4, 5, 6] }),
159
164
  );
160
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
165
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
161
166
  });
162
167
 
163
- test('returns true for a realistic actor.json with only title/description change', () => {
168
+ test('returns true for a realistic actor.json with only title/description change', async () => {
164
169
  mockGitCalls(
165
170
  JSON.stringify({
166
171
  actorSpecification: 1,
@@ -177,10 +182,10 @@ describe('isCosmeticOnlyJsonSchemaChange', () => {
177
182
  version: '1.0',
178
183
  }),
179
184
  );
180
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
185
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
181
186
  });
182
187
 
183
- test('returns true when fields under properties are reordered', () => {
188
+ test('returns true when fields under properties are reordered', async () => {
184
189
  mockGitCalls(
185
190
  JSON.stringify({
186
191
  type: 'object',
@@ -191,22 +196,22 @@ describe('isCosmeticOnlyJsonSchemaChange', () => {
191
196
  properties: { fieldC: { type: 'boolean' }, fieldA: { type: 'string' }, fieldB: { type: 'integer' } },
192
197
  }),
193
198
  );
194
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(true);
199
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(true);
195
200
  });
196
201
 
197
- test('returns false for a realistic actor.json with version change', () => {
202
+ test('returns false for a realistic actor.json with version change', async () => {
198
203
  mockGitCalls(
199
204
  JSON.stringify({ actorSpecification: 1, name: 'my-actor', title: 'My Actor', version: '1.0' }),
200
205
  JSON.stringify({ actorSpecification: 1, name: 'my-actor', title: 'My Actor', version: '1.1' }),
201
206
  );
202
- expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).toBe(false);
207
+ await expect(isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json')).resolves.toBe(false);
203
208
  });
204
209
 
205
- it('uses correct git refs: first-commit parent for old, last commit for new', () => {
210
+ it('uses correct git refs: first-commit parent for old, last commit for new', async () => {
206
211
  const json = JSON.stringify({});
207
212
  mockGitCalls(json, json);
208
- isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json');
209
- expect(gitCommandSpy).toHaveBeenCalledWith('git show Commit1~:actors/foo/actor.json');
210
- expect(gitCommandSpy).toHaveBeenCalledWith('git show Commit3:actors/foo/actor.json');
213
+ await isCosmeticOnlyJsonSchemaChange(commits, 'actors/foo/actor.json');
214
+ expect(gitCommandSpy).toHaveBeenCalledWith(['show', 'Commit1~:actors/foo/actor.json']);
215
+ expect(gitCommandSpy).toHaveBeenCalledWith(['show', 'Commit3:actors/foo/actor.json']);
211
216
  });
212
217
  });
@@ -0,0 +1,71 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+
4
+ import { describe, expect, it } from 'vitest';
5
+
6
+ import { getGitignoredPaths, GitCommandError, listRepoFilePaths, runGitCommand } from '../../../bin/utils.js';
7
+
8
+ describe('runGitCommand', () => {
9
+ it('returns trimmed Git output', async () => {
10
+ await expect(
11
+ runGitCommand(['rev-parse', '--is-inside-work-tree'], {
12
+ cwd: process.cwd(),
13
+ operation: 'check whether the current directory is in a Git repository',
14
+ }),
15
+ ).resolves.toBe('true');
16
+ });
17
+
18
+ it('rejects on git exit code, not stderr', async () => {
19
+ const error: unknown = await runGitCommand(['rev-parse', '--verify', '--quiet'], {
20
+ cwd: process.cwd(),
21
+ operation: 'verify a commit',
22
+ }).catch((failure: unknown) => failure);
23
+ expect(error).toBeInstanceOf(GitCommandError);
24
+ if (error instanceof GitCommandError) {
25
+ expect(error.exitCode).toBe(1);
26
+ expect(error.gitError).toContain('Command failed: git rev-parse --verify --quiet');
27
+ expect(error.message).toContain('Failed to verify a commit.\nCommand:');
28
+ expect(error.stack).toContain('Failed to verify a commit.');
29
+ expect(error.cause).toBeInstanceOf(Error);
30
+ }
31
+ });
32
+
33
+ it('passes arguments literally and rejects Git failures', async () => {
34
+ await expect(
35
+ runGitCommand(['rev-parse', '--verify', 'HEAD; echo injected'], {
36
+ operation: 'verify a commit reference',
37
+ }),
38
+ ).rejects.toThrow('Command: git "rev-parse" "--verify" "HEAD; echo injected"\nGit error:');
39
+ });
40
+ });
41
+
42
+ describe('listRepoFilePaths', () => {
43
+ it('lists files under a subdirectory relative to the repository root', async () => {
44
+ const repoRoot = process.cwd();
45
+ await expect(listRepoFilePaths(repoRoot, path.join(repoRoot, 'bin'))).resolves.toContain('bin/utils.ts');
46
+ });
47
+ });
48
+
49
+ describe('getGitignoredPaths', () => {
50
+ it('returns an empty set for no paths', async () => {
51
+ await expect(getGitignoredPaths([])).resolves.toStrictEqual(new Set());
52
+ });
53
+
54
+ it('returns only paths ignored by the repository rules', async () => {
55
+ await expect(
56
+ getGitignoredPaths(['node_modules/example.js', 'bin/utils.ts', '.DS_Store'], process.cwd()),
57
+ ).resolves.toStrictEqual(new Set(['node_modules/example.js', '.DS_Store']));
58
+ });
59
+
60
+ it('returns an empty set when Git exits with code 1', async () => {
61
+ await expect(getGitignoredPaths(['bin/utils.ts'], process.cwd())).resolves.toStrictEqual(new Set());
62
+ });
63
+
64
+ it('rejects unexpected Git errors', async () => {
65
+ await expect(getGitignoredPaths(['bin/utils.ts'], os.tmpdir())).rejects.toThrow(
66
+ 'Failed to check which repository paths are ignored.\n' +
67
+ 'Command: git "check-ignore" "--" "bin/utils.ts"\n' +
68
+ 'Git error: fatal: not a git repository',
69
+ );
70
+ });
71
+ });