Skip to content

Commit b823a3e

Browse files
authored
11 csv writer (#16)
* add file writer to implement fs this way we can pass in to CsvWriter and test accordingly * define custom errors * define types to use * create a formatter helper * create a helper for working with headers * create writer for orchestrating the write to csv output * create a factory that can support factory pattern to produce writers * update to export types * generate tests for classes we've created * address coverage and failing tests * add doc * add async generator example * add jsdoc strings
1 parent c271ade commit b823a3e

17 files changed

+2646
-89
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ pnpm install
3838
```
3939
outport/
4040
├── .github/ # GitHub Actions workflows and configs
41+
├── docs/ # Documentation
42+
│ └── csv-writer.md # CSV Writer usage guide
4143
├── src/ # Source TypeScript files
4244
│ ├── index.ts # Main entry point
4345
│ └── index.test.ts # Test files
@@ -51,6 +53,10 @@ outport/
5153
└── .prettierrc # Prettier configuration
5254
```
5355

56+
## 📚 Documentation
57+
58+
- **[CSV Writer Guide](docs/csv-writer.md)** - Examples and usage patterns for the CSV writer
59+
5460
## 🧪 Testing
5561

5662
This project uses [Vitest](https://vitest.dev/) for testing with the following features:

__tests__/io/FileWriter.test.ts

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
import { describe, it, expect, beforeEach, afterEach, afterAll } from 'vitest';
2+
import * as fs from 'node:fs';
3+
import * as path from 'node:path';
4+
import { NodeFileWriter } from '../../src/io/FileWriter';
5+
6+
describe('NodeFileWriter', () => {
7+
const testDir = path.join(process.cwd(), '__tests__', 'temp', 'file-writer');
8+
let testFile: string;
9+
let fileWriter: NodeFileWriter;
10+
11+
beforeEach(() => {
12+
// Create test directory
13+
if (!fs.existsSync(testDir)) {
14+
fs.mkdirSync(testDir, { recursive: true });
15+
}
16+
// Create unique test file for each test
17+
testFile = path.join(testDir, `test-${Date.now()}-${Math.random()}.txt`);
18+
fileWriter = new NodeFileWriter();
19+
});
20+
21+
afterEach(() => {
22+
// Clean up test files
23+
if (fs.existsSync(testFile)) {
24+
fs.unlinkSync(testFile);
25+
}
26+
});
27+
28+
describe('writeSync', () => {
29+
it('should write content to a new file successfully', () => {
30+
// Arrange
31+
const content = 'Hello, World!';
32+
33+
// Act
34+
const result = fileWriter.writeSync(testFile, content);
35+
36+
// Assert
37+
expect(result.success).toBe(true);
38+
expect(fs.existsSync(testFile)).toBe(true);
39+
expect(fs.readFileSync(testFile, 'utf-8')).toBe(content);
40+
});
41+
42+
it('should overwrite existing file content', () => {
43+
// Arrange
44+
fs.writeFileSync(testFile, 'Old content');
45+
const newContent = 'New content';
46+
47+
// Act
48+
const result = fileWriter.writeSync(testFile, newContent);
49+
50+
// Assert
51+
expect(result.success).toBe(true);
52+
expect(fs.readFileSync(testFile, 'utf-8')).toBe(newContent);
53+
});
54+
55+
it('should return error result for invalid path', () => {
56+
// Arrange
57+
const invalidPath = '/invalid/path/that/does/not/exist/file.txt';
58+
59+
// Act
60+
const result = fileWriter.writeSync(invalidPath, 'content');
61+
62+
// Assert
63+
expect(result.success).toBe(false);
64+
if (!result.success) {
65+
expect(result.error.message).toContain('Failed to write file');
66+
}
67+
});
68+
});
69+
70+
describe('write (async)', () => {
71+
it('should write content to a new file successfully', async () => {
72+
// Arrange
73+
const content = 'Hello, Async World!';
74+
75+
// Act
76+
const result = await fileWriter.write(testFile, content);
77+
78+
// Assert
79+
expect(result.success).toBe(true);
80+
expect(fs.existsSync(testFile)).toBe(true);
81+
expect(fs.readFileSync(testFile, 'utf-8')).toBe(content);
82+
});
83+
84+
it('should overwrite existing file content', async () => {
85+
// Arrange
86+
fs.writeFileSync(testFile, 'Old content');
87+
const newContent = 'New async content';
88+
89+
// Act
90+
const result = await fileWriter.write(testFile, newContent);
91+
92+
// Assert
93+
expect(result.success).toBe(true);
94+
expect(fs.readFileSync(testFile, 'utf-8')).toBe(newContent);
95+
});
96+
97+
it('should return error result for invalid path', async () => {
98+
// Arrange
99+
const invalidPath = '/invalid/path/that/does/not/exist/file.txt';
100+
101+
// Act
102+
const result = await fileWriter.write(invalidPath, 'content');
103+
104+
// Assert
105+
expect(result.success).toBe(false);
106+
if (!result.success) {
107+
expect(result.error.message).toContain('Failed to write file');
108+
}
109+
});
110+
});
111+
112+
describe('appendSync', () => {
113+
it('should append content to existing file', () => {
114+
// Arrange
115+
const initialContent = 'Line 1\n';
116+
const appendContent = 'Line 2\n';
117+
fs.writeFileSync(testFile, initialContent);
118+
119+
// Act
120+
const result = fileWriter.appendSync(testFile, appendContent);
121+
122+
// Assert
123+
expect(result.success).toBe(true);
124+
expect(fs.readFileSync(testFile, 'utf-8')).toBe(initialContent + appendContent);
125+
});
126+
127+
it('should create file if it does not exist', () => {
128+
// Arrange
129+
const content = 'New file content\n';
130+
131+
// Act
132+
const result = fileWriter.appendSync(testFile, content);
133+
134+
// Assert
135+
expect(result.success).toBe(true);
136+
expect(fs.existsSync(testFile)).toBe(true);
137+
expect(fs.readFileSync(testFile, 'utf-8')).toBe(content);
138+
});
139+
140+
it('should return error result for invalid path', () => {
141+
// Arrange
142+
const invalidPath = '/invalid/path/that/does/not/exist/file.txt';
143+
144+
// Act
145+
const result = fileWriter.appendSync(invalidPath, 'content');
146+
147+
// Assert
148+
expect(result.success).toBe(false);
149+
if (!result.success) {
150+
expect(result.error.message).toContain('Failed to append to file');
151+
}
152+
});
153+
});
154+
155+
describe('append (async)', () => {
156+
it('should append content to existing file', async () => {
157+
// Arrange
158+
const initialContent = 'Line 1\n';
159+
const appendContent = 'Line 2\n';
160+
fs.writeFileSync(testFile, initialContent);
161+
162+
// Act
163+
const result = await fileWriter.append(testFile, appendContent);
164+
165+
// Assert
166+
expect(result.success).toBe(true);
167+
expect(fs.readFileSync(testFile, 'utf-8')).toBe(initialContent + appendContent);
168+
});
169+
170+
it('should create file if it does not exist', async () => {
171+
// Arrange
172+
const content = 'New async file content\n';
173+
174+
// Act
175+
const result = await fileWriter.append(testFile, content);
176+
177+
// Assert
178+
expect(result.success).toBe(true);
179+
expect(fs.existsSync(testFile)).toBe(true);
180+
expect(fs.readFileSync(testFile, 'utf-8')).toBe(content);
181+
});
182+
183+
it('should return error result for invalid path', async () => {
184+
// Arrange
185+
const invalidPath = '/invalid/path/that/does/not/exist/file.txt';
186+
187+
// Act
188+
const result = await fileWriter.append(invalidPath, 'content');
189+
190+
// Assert
191+
expect(result.success).toBe(false);
192+
if (!result.success) {
193+
expect(result.error.message).toContain('Failed to append to file');
194+
}
195+
});
196+
});
197+
198+
describe('existsSync', () => {
199+
it('should return true for existing file', () => {
200+
// Arrange
201+
fs.writeFileSync(testFile, 'content');
202+
203+
// Act
204+
const result = fileWriter.existsSync(testFile);
205+
206+
// Assert
207+
expect(result).toBe(true);
208+
});
209+
210+
it('should return false for non-existing file', () => {
211+
// Act
212+
const result = fileWriter.existsSync(testFile);
213+
214+
// Assert
215+
expect(result).toBe(false);
216+
});
217+
});
218+
219+
describe('exists (async)', () => {
220+
it('should return true for existing file', async () => {
221+
// Arrange
222+
fs.writeFileSync(testFile, 'content');
223+
224+
// Act
225+
const result = await fileWriter.exists(testFile);
226+
227+
// Assert
228+
expect(result).toBe(true);
229+
});
230+
231+
it('should return false for non-existing file', async () => {
232+
// Act
233+
const result = await fileWriter.exists(testFile);
234+
235+
// Assert
236+
expect(result).toBe(false);
237+
});
238+
});
239+
240+
// Cleanup temp directory after all tests
241+
afterAll(() => {
242+
if (fs.existsSync(testDir)) {
243+
fs.rmSync(testDir, { recursive: true, force: true });
244+
}
245+
});
246+
});

0 commit comments

Comments
 (0)