repo-cloak-cli 1.2.4 → 1.3.2

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.
@@ -1,94 +1,94 @@
1
- /**
2
- * Copier Tests
3
- */
4
-
5
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
- import { copyFile, copyFileWithTransform } from '../src/core/copier.js';
7
- import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'fs';
8
- import { join } from 'path';
9
- import { tmpdir } from 'os';
10
-
11
- describe('Copier Module', () => {
12
- let testDir;
13
- let sourceDir;
14
- let destDir;
15
-
16
- beforeEach(() => {
17
- testDir = join(tmpdir(), `repo-cloak-copier-test-${Date.now()}`);
18
- sourceDir = join(testDir, 'source');
19
- destDir = join(testDir, 'dest');
20
- mkdirSync(sourceDir, { recursive: true });
21
- mkdirSync(destDir, { recursive: true });
22
- });
23
-
24
- afterEach(() => {
25
- if (existsSync(testDir)) {
26
- rmSync(testDir, { recursive: true, force: true });
27
- }
28
- });
29
-
30
- describe('copyFile', () => {
31
- it('should copy a file to destination', () => {
32
- const sourceFile = join(sourceDir, 'test.txt');
33
- const destFile = join(destDir, 'test.txt');
34
-
35
- writeFileSync(sourceFile, 'Hello World');
36
- copyFile(sourceFile, destFile);
37
-
38
- expect(existsSync(destFile)).toBe(true);
39
- expect(readFileSync(destFile, 'utf-8')).toBe('Hello World');
40
- });
41
-
42
- it('should create nested directories', () => {
43
- const sourceFile = join(sourceDir, 'test.txt');
44
- const destFile = join(destDir, 'nested', 'deep', 'test.txt');
45
-
46
- writeFileSync(sourceFile, 'Content');
47
- copyFile(sourceFile, destFile);
48
-
49
- expect(existsSync(destFile)).toBe(true);
50
- });
51
- });
52
-
53
- describe('copyFileWithTransform', () => {
54
- it('should transform content during copy', () => {
55
- const sourceFile = join(sourceDir, 'code.js');
56
- const destFile = join(destDir, 'code.js');
57
-
58
- writeFileSync(sourceFile, 'const company = "Cuviva";');
59
-
60
- const transform = (content) => content.replace(/Cuviva/g, 'ABCCompany');
61
- const result = copyFileWithTransform(sourceFile, destFile, transform);
62
-
63
- expect(result.transformed).toBe(true);
64
- expect(readFileSync(destFile, 'utf-8')).toBe('const company = "ABCCompany";');
65
- });
66
-
67
- it('should report no transformation when content unchanged', () => {
68
- const sourceFile = join(sourceDir, 'code.js');
69
- const destFile = join(destDir, 'code.js');
70
-
71
- writeFileSync(sourceFile, 'const x = 1;');
72
-
73
- const transform = (content) => content.replace(/Cuviva/g, 'ABCCompany');
74
- const result = copyFileWithTransform(sourceFile, destFile, transform);
75
-
76
- expect(result.transformed).toBe(false);
77
- });
78
-
79
- it('should handle binary files by copying as-is', () => {
80
- const sourceFile = join(sourceDir, 'image.png');
81
- const destFile = join(destDir, 'image.png');
82
-
83
- // Create a simple binary-like file
84
- const buffer = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x00]);
85
- writeFileSync(sourceFile, buffer);
86
-
87
- const transform = (content) => content.replace(/test/g, 'replaced');
88
- const result = copyFileWithTransform(sourceFile, destFile, transform);
89
-
90
- expect(result.transformed).toBe(false);
91
- expect(existsSync(destFile)).toBe(true);
92
- });
93
- });
94
- });
1
+ /**
2
+ * Copier Tests
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+ import { copyFile, copyFileWithTransform } from '../src/core/copier.js';
7
+ import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { tmpdir } from 'os';
10
+
11
+ describe('Copier Module', () => {
12
+ let testDir;
13
+ let sourceDir;
14
+ let destDir;
15
+
16
+ beforeEach(() => {
17
+ testDir = join(tmpdir(), `repo-cloak-copier-test-${Date.now()}`);
18
+ sourceDir = join(testDir, 'source');
19
+ destDir = join(testDir, 'dest');
20
+ mkdirSync(sourceDir, { recursive: true });
21
+ mkdirSync(destDir, { recursive: true });
22
+ });
23
+
24
+ afterEach(() => {
25
+ if (existsSync(testDir)) {
26
+ rmSync(testDir, { recursive: true, force: true });
27
+ }
28
+ });
29
+
30
+ describe('copyFile', () => {
31
+ it('should copy a file to destination', () => {
32
+ const sourceFile = join(sourceDir, 'test.txt');
33
+ const destFile = join(destDir, 'test.txt');
34
+
35
+ writeFileSync(sourceFile, 'Hello World');
36
+ copyFile(sourceFile, destFile);
37
+
38
+ expect(existsSync(destFile)).toBe(true);
39
+ expect(readFileSync(destFile, 'utf-8')).toBe('Hello World');
40
+ });
41
+
42
+ it('should create nested directories', () => {
43
+ const sourceFile = join(sourceDir, 'test.txt');
44
+ const destFile = join(destDir, 'nested', 'deep', 'test.txt');
45
+
46
+ writeFileSync(sourceFile, 'Content');
47
+ copyFile(sourceFile, destFile);
48
+
49
+ expect(existsSync(destFile)).toBe(true);
50
+ });
51
+ });
52
+
53
+ describe('copyFileWithTransform', () => {
54
+ it('should transform content during copy', () => {
55
+ const sourceFile = join(sourceDir, 'code.js');
56
+ const destFile = join(destDir, 'code.js');
57
+
58
+ writeFileSync(sourceFile, 'const company = "Cuviva";');
59
+
60
+ const transform = (content) => content.replace(/Cuviva/g, 'ABCCompany');
61
+ const result = copyFileWithTransform(sourceFile, destFile, transform);
62
+
63
+ expect(result.transformed).toBe(true);
64
+ expect(readFileSync(destFile, 'utf-8')).toBe('const company = "ABCCompany";');
65
+ });
66
+
67
+ it('should report no transformation when content unchanged', () => {
68
+ const sourceFile = join(sourceDir, 'code.js');
69
+ const destFile = join(destDir, 'code.js');
70
+
71
+ writeFileSync(sourceFile, 'const x = 1;');
72
+
73
+ const transform = (content) => content.replace(/Cuviva/g, 'ABCCompany');
74
+ const result = copyFileWithTransform(sourceFile, destFile, transform);
75
+
76
+ expect(result.transformed).toBe(false);
77
+ });
78
+
79
+ it('should handle binary files by copying as-is', () => {
80
+ const sourceFile = join(sourceDir, 'image.png');
81
+ const destFile = join(destDir, 'image.png');
82
+
83
+ // Create a simple binary-like file
84
+ const buffer = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x00]);
85
+ writeFileSync(sourceFile, buffer);
86
+
87
+ const transform = (content) => content.replace(/test/g, 'replaced');
88
+ const result = copyFileWithTransform(sourceFile, destFile, transform);
89
+
90
+ expect(result.transformed).toBe(false);
91
+ expect(existsSync(destFile)).toBe(true);
92
+ });
93
+ });
94
+ });
@@ -1,106 +1,106 @@
1
- /**
2
- * Crypto Tests
3
- */
4
-
5
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
- import { encrypt, decrypt, encryptReplacements, decryptReplacements } from '../src/core/crypto.js';
7
- import { existsSync, rmSync, mkdirSync } from 'fs';
8
- import { join } from 'path';
9
- import { homedir } from 'os';
10
-
11
- describe('Crypto Module', () => {
12
- const testSecret = 'test-secret-key-for-unit-tests-1234567890';
13
-
14
- describe('encrypt/decrypt', () => {
15
- it('should encrypt and decrypt a string', () => {
16
- const original = 'Hello World';
17
- const encrypted = encrypt(original, testSecret);
18
- const decrypted = decrypt(encrypted, testSecret);
19
-
20
- expect(decrypted).toBe(original);
21
- });
22
-
23
- it('should produce different encrypted output each time (random IV)', () => {
24
- const original = 'Same input';
25
- const encrypted1 = encrypt(original, testSecret);
26
- const encrypted2 = encrypt(original, testSecret);
27
-
28
- expect(encrypted1).not.toBe(encrypted2);
29
- });
30
-
31
- it('should return null for wrong secret', () => {
32
- const original = 'Secret message';
33
- const encrypted = encrypt(original, testSecret);
34
- const decrypted = decrypt(encrypted, 'wrong-secret');
35
-
36
- expect(decrypted).toBeNull();
37
- });
38
-
39
- it('should handle special characters', () => {
40
- const original = 'Special: @#$%^&*()_+ 日本語 🎭';
41
- const encrypted = encrypt(original, testSecret);
42
- const decrypted = decrypt(encrypted, testSecret);
43
-
44
- expect(decrypted).toBe(original);
45
- });
46
-
47
- it('should handle empty string or return null for empty', () => {
48
- const original = '';
49
- const encrypted = encrypt(original, testSecret);
50
- const decrypted = decrypt(encrypted, testSecret);
51
-
52
- // Empty string may return empty or null depending on cipher
53
- expect(decrypted === '' || decrypted === null).toBe(true);
54
- });
55
-
56
- it('should handle long strings', () => {
57
- const original = 'A'.repeat(10000);
58
- const encrypted = encrypt(original, testSecret);
59
- const decrypted = decrypt(encrypted, testSecret);
60
-
61
- expect(decrypted).toBe(original);
62
- });
63
- });
64
-
65
- describe('encryptReplacements/decryptReplacements', () => {
66
- it('should encrypt only the original field', () => {
67
- const replacements = [
68
- { original: 'Cuviva', replacement: 'ABCCompany' },
69
- { original: 'Secret', replacement: 'Public' }
70
- ];
71
-
72
- const encrypted = encryptReplacements(replacements, testSecret);
73
-
74
- // Replacement should still be visible
75
- expect(encrypted[0].replacement).toBe('ABCCompany');
76
- expect(encrypted[1].replacement).toBe('Public');
77
-
78
- // Original should be encrypted (contains colons from format)
79
- expect(encrypted[0].original).toContain(':');
80
- expect(encrypted[0].encrypted).toBe(true);
81
- });
82
-
83
- it('should decrypt back to original', () => {
84
- const replacements = [
85
- { original: 'Cuviva', replacement: 'ABCCompany' }
86
- ];
87
-
88
- const encrypted = encryptReplacements(replacements, testSecret);
89
- const decrypted = decryptReplacements(encrypted, testSecret);
90
-
91
- expect(decrypted[0].original).toBe('Cuviva');
92
- expect(decrypted[0].replacement).toBe('ABCCompany');
93
- });
94
-
95
- it('should mark failed decryptions', () => {
96
- const replacements = [
97
- { original: 'Test', replacement: 'Demo' }
98
- ];
99
-
100
- const encrypted = encryptReplacements(replacements, testSecret);
101
- const decrypted = decryptReplacements(encrypted, 'wrong-secret');
102
-
103
- expect(decrypted[0].decryptFailed).toBe(true);
104
- });
105
- });
106
- });
1
+ /**
2
+ * Crypto Tests
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+ import { encrypt, decrypt, encryptReplacements, decryptReplacements } from '../src/core/crypto.js';
7
+ import { existsSync, rmSync, mkdirSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { homedir } from 'os';
10
+
11
+ describe('Crypto Module', () => {
12
+ const testSecret = 'test-secret-key-for-unit-tests-1234567890';
13
+
14
+ describe('encrypt/decrypt', () => {
15
+ it('should encrypt and decrypt a string', () => {
16
+ const original = 'Hello World';
17
+ const encrypted = encrypt(original, testSecret);
18
+ const decrypted = decrypt(encrypted, testSecret);
19
+
20
+ expect(decrypted).toBe(original);
21
+ });
22
+
23
+ it('should produce different encrypted output each time (random IV)', () => {
24
+ const original = 'Same input';
25
+ const encrypted1 = encrypt(original, testSecret);
26
+ const encrypted2 = encrypt(original, testSecret);
27
+
28
+ expect(encrypted1).not.toBe(encrypted2);
29
+ });
30
+
31
+ it('should return null for wrong secret', () => {
32
+ const original = 'Secret message';
33
+ const encrypted = encrypt(original, testSecret);
34
+ const decrypted = decrypt(encrypted, 'wrong-secret');
35
+
36
+ expect(decrypted).toBeNull();
37
+ });
38
+
39
+ it('should handle special characters', () => {
40
+ const original = 'Special: @#$%^&*()_+ 日本語 🎭';
41
+ const encrypted = encrypt(original, testSecret);
42
+ const decrypted = decrypt(encrypted, testSecret);
43
+
44
+ expect(decrypted).toBe(original);
45
+ });
46
+
47
+ it('should handle empty string or return null for empty', () => {
48
+ const original = '';
49
+ const encrypted = encrypt(original, testSecret);
50
+ const decrypted = decrypt(encrypted, testSecret);
51
+
52
+ // Empty string may return empty or null depending on cipher
53
+ expect(decrypted === '' || decrypted === null).toBe(true);
54
+ });
55
+
56
+ it('should handle long strings', () => {
57
+ const original = 'A'.repeat(10000);
58
+ const encrypted = encrypt(original, testSecret);
59
+ const decrypted = decrypt(encrypted, testSecret);
60
+
61
+ expect(decrypted).toBe(original);
62
+ });
63
+ });
64
+
65
+ describe('encryptReplacements/decryptReplacements', () => {
66
+ it('should encrypt only the original field', () => {
67
+ const replacements = [
68
+ { original: 'Cuviva', replacement: 'ABCCompany' },
69
+ { original: 'Secret', replacement: 'Public' }
70
+ ];
71
+
72
+ const encrypted = encryptReplacements(replacements, testSecret);
73
+
74
+ // Replacement should still be visible
75
+ expect(encrypted[0].replacement).toBe('ABCCompany');
76
+ expect(encrypted[1].replacement).toBe('Public');
77
+
78
+ // Original should be encrypted (contains colons from format)
79
+ expect(encrypted[0].original).toContain(':');
80
+ expect(encrypted[0].encrypted).toBe(true);
81
+ });
82
+
83
+ it('should decrypt back to original', () => {
84
+ const replacements = [
85
+ { original: 'Cuviva', replacement: 'ABCCompany' }
86
+ ];
87
+
88
+ const encrypted = encryptReplacements(replacements, testSecret);
89
+ const decrypted = decryptReplacements(encrypted, testSecret);
90
+
91
+ expect(decrypted[0].original).toBe('Cuviva');
92
+ expect(decrypted[0].replacement).toBe('ABCCompany');
93
+ });
94
+
95
+ it('should mark failed decryptions', () => {
96
+ const replacements = [
97
+ { original: 'Test', replacement: 'Demo' }
98
+ ];
99
+
100
+ const encrypted = encryptReplacements(replacements, testSecret);
101
+ const decrypted = decryptReplacements(encrypted, 'wrong-secret');
102
+
103
+ expect(decrypted[0].decryptFailed).toBe(true);
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Git Module Tests
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+ import { isGitRepo, getChangedFiles } from '../src/core/git.js';
7
+ import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { tmpdir } from 'os';
10
+ import { execSync } from 'child_process';
11
+
12
+ describe('Git Module', () => {
13
+ let testDir;
14
+
15
+ function initGitRepo(dir) {
16
+ execSync('git init', { cwd: dir });
17
+ execSync('git config user.name "Test User"', { cwd: dir });
18
+ execSync('git config user.email "test@example.com"', { cwd: dir });
19
+ }
20
+
21
+ beforeEach(() => {
22
+ testDir = join(tmpdir(), `repo-cloak-git-test-${Date.now()}`);
23
+ mkdirSync(testDir, { recursive: true });
24
+ });
25
+
26
+ afterEach(() => {
27
+ if (existsSync(testDir)) {
28
+ try {
29
+ rmSync(testDir, { recursive: true, force: true });
30
+ } catch (e) {
31
+ // Ignore cleanup errors
32
+ }
33
+ }
34
+ });
35
+
36
+ describe('isGitRepo', () => {
37
+ it('should return true for git repository', () => {
38
+ initGitRepo(testDir);
39
+ expect(isGitRepo(testDir)).toBe(true);
40
+ });
41
+
42
+ it('should return false for non-git directory', () => {
43
+ expect(isGitRepo(testDir)).toBe(false);
44
+ });
45
+ });
46
+
47
+ describe('getChangedFiles', () => {
48
+ it('should return empty list for clean repo', async () => {
49
+ initGitRepo(testDir);
50
+ const files = await getChangedFiles(testDir);
51
+ expect(files).toEqual([]);
52
+ });
53
+
54
+ it('should detect untracked files', async () => {
55
+ initGitRepo(testDir);
56
+ writeFileSync(join(testDir, 'newfile.txt'), 'content');
57
+
58
+ const files = await getChangedFiles(testDir);
59
+ expect(files).toContain('newfile.txt');
60
+ });
61
+
62
+ it('should detect modified files', async () => {
63
+ initGitRepo(testDir);
64
+ writeFileSync(join(testDir, 'test.txt'), 'initial');
65
+ execSync('git add test.txt', { cwd: testDir });
66
+ execSync('git commit -m "initial"', { cwd: testDir });
67
+
68
+ writeFileSync(join(testDir, 'test.txt'), 'changed');
69
+
70
+ const files = await getChangedFiles(testDir);
71
+ expect(files).toContain('test.txt');
72
+ });
73
+
74
+ it('should expand untracked directories', async () => {
75
+ initGitRepo(testDir);
76
+ const deployDir = join(testDir, 'deploy');
77
+ mkdirSync(deployDir);
78
+ writeFileSync(join(deployDir, 'config.yml'), 'config');
79
+ writeFileSync(join(deployDir, 'script.sh'), 'script');
80
+
81
+ // git status --porcelain shows "?? deploy/"
82
+ // We want ["deploy/config.yml", "deploy/script.sh"]
83
+
84
+ const files = await getChangedFiles(testDir);
85
+ expect(files).toContain('deploy/config.yml'); // Using forward slash for cross-platform expectation in test
86
+ expect(files).toContain('deploy/script.sh');
87
+ expect(files).toHaveLength(2);
88
+ });
89
+
90
+ it('should handle renamed files', async () => {
91
+ initGitRepo(testDir);
92
+ writeFileSync(join(testDir, 'old.txt'), 'content');
93
+ execSync('git add old.txt', { cwd: testDir });
94
+ execSync('git commit -m "initial"', { cwd: testDir });
95
+
96
+ execSync('git mv old.txt new.txt', { cwd: testDir });
97
+
98
+ const files = await getChangedFiles(testDir);
99
+ expect(files).toContain('new.txt');
100
+ expect(files).not.toContain('old.txt');
101
+ });
102
+ });
103
+ });