Skip to content

Commit b66c759

Browse files
committed
stash unit tests and integration tests
1 parent 6804c0d commit b66c759

37 files changed

Lines changed: 3760 additions & 66 deletions

TESTING.md

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
# Testing Guide for pos-cli
2+
3+
This document describes the testing architecture, conventions, and how to run and write tests.
4+
5+
## Overview
6+
7+
Tests are organized into two categories:
8+
9+
- **Unit tests** (`test/unit/`) - Fast, isolated tests that mock external dependencies
10+
- **Integration tests** (`test/integration/`) - End-to-end tests that require real platformOS credentials
11+
12+
## Running Tests
13+
14+
```bash
15+
# Run all tests
16+
npm test
17+
18+
# Run only unit tests (fast, no credentials needed)
19+
npm run test:unit
20+
21+
# Run only integration tests (requires credentials)
22+
npm run test:integration
23+
24+
# Watch mode for development
25+
npm run test:watch
26+
```
27+
28+
## Test Framework
29+
30+
- **Test Runner**: [Vitest](https://vitest.dev/) v4.x
31+
- **HTTP Mocking**: [nock](https://github.com/nock/nock) for intercepting HTTP requests
32+
- **Module System**: ESM (ES Modules)
33+
34+
## Directory Structure
35+
36+
```
37+
test/
38+
├── unit/ # Unit tests (mocked dependencies)
39+
│ ├── sync.test.js # shouldBeSynced function tests
40+
│ ├── deploy.test.js # Deploy logic tests with mocked API
41+
│ ├── templates.test.js # Template processing tests
42+
│ ├── manifest.test.js # Asset manifest generation
43+
│ ├── dependencies.test.js # Module dependency resolution
44+
│ └── lib/ # Library-specific unit tests
45+
├── integration/ # Integration tests (real API calls)
46+
│ ├── deploy.test.js # Full deploy workflow
47+
│ ├── sync.test.js # File sync with real instance
48+
│ ├── modules-*.test.js # Module operations
49+
│ └── logs.test.js # Log streaming
50+
├── fixtures/ # Test data and project structures
51+
│ ├── deploy/ # Deploy test projects
52+
│ ├── modules/ # Module test data
53+
│ └── audit/ # Audit rule test cases
54+
└── utils/ # Shared test utilities
55+
├── credentials.js # Credential management
56+
├── exec.js # CLI execution helper
57+
└── cliPath.js # Path to CLI binary
58+
```
59+
60+
## Writing Tests
61+
62+
### Unit Tests
63+
64+
Unit tests should:
65+
- Mock all external dependencies (HTTP, file system when needed)
66+
- Be fast (< 1 second per test)
67+
- Test isolated functionality
68+
- Not require environment credentials
69+
70+
```javascript
71+
import { describe, test, expect, vi, beforeEach } from 'vitest';
72+
import nock from 'nock';
73+
74+
// Mock dependencies
75+
vi.mock('#lib/logger.js', () => ({
76+
default: { Debug: vi.fn(), Warn: vi.fn(), Error: vi.fn(), Info: vi.fn() }
77+
}));
78+
79+
describe('Feature', () => {
80+
beforeEach(() => {
81+
vi.clearAllMocks();
82+
nock.cleanAll();
83+
});
84+
85+
test('handles API response correctly', async () => {
86+
// Mock the HTTP call
87+
nock('https://example.com')
88+
.post('/api/app_builder/marketplace_releases')
89+
.reply(200, { id: 123, status: 'pending' });
90+
91+
// Test the functionality
92+
const result = await someFunction();
93+
expect(result.id).toBe(123);
94+
});
95+
});
96+
```
97+
98+
### Integration Tests
99+
100+
Integration tests should:
101+
- Import `dotenv/config` at the top to load credentials
102+
- Call `requireRealCredentials()` at the start of tests needing real API
103+
- Use extended timeouts for API operations
104+
- Clean up any created resources
105+
106+
```javascript
107+
import 'dotenv/config';
108+
import { describe, test, expect, vi } from 'vitest';
109+
import { requireRealCredentials } from '#test/utils/credentials';
110+
111+
vi.setConfig({ testTimeout: 40000 }); // Extended timeout
112+
113+
describe('Deploy', () => {
114+
test('deploys successfully', async () => {
115+
requireRealCredentials();
116+
117+
// Test with real API
118+
const { stdout } = await exec(`${cliPath} deploy`);
119+
expect(stdout).toMatch('Deploy succeeded');
120+
});
121+
});
122+
```
123+
124+
## HTTP Mocking Strategy
125+
126+
### Approach: Record & Replay
127+
128+
We use HTTP mocking to create unit test versions of integration tests. This approach:
129+
130+
1. **Records** real API responses during integration test development
131+
2. **Replays** those responses in unit tests for fast, reliable execution
132+
3. **Allows** re-running against real APIs when needed for validation
133+
134+
### Libraries Evaluated
135+
136+
| Library | Description | Chosen |
137+
|---------|-------------|--------|
138+
| [nock](https://github.com/nock/nock) | HTTP mocking with declarative API | ✅ Primary |
139+
| [MSW](https://mswjs.io/) | Network-level interception | Alternative |
140+
| [Polly.JS](https://netflix.github.io/pollyjs/) | Record/replay with persistence | For complex scenarios |
141+
142+
We chose **nock** because:
143+
- Simple, declarative API
144+
- Native fetch support (via @mswjs/interceptors)
145+
- Lightweight, no additional setup needed
146+
- Well-suited for testing HTTP clients
147+
148+
### Mock Data Organization
149+
150+
Mock responses are stored alongside tests or in dedicated fixtures:
151+
152+
```
153+
test/
154+
├── unit/
155+
│ ├── deploy.test.js
156+
│ └── __mocks__/ # Recorded API responses
157+
│ └── deploy/
158+
│ ├── success.json
159+
│ └── error.json
160+
```
161+
162+
## Credential Management
163+
164+
### For Unit Tests
165+
166+
Use example credentials from `test/utils/credentials.js`:
167+
168+
```javascript
169+
import { exampleCredentials } from '#test/utils/credentials';
170+
// { MPKIT_URL: 'https://example.com', MPKIT_TOKEN: 'test-token', ... }
171+
```
172+
173+
### For Integration Tests
174+
175+
1. Copy `.env.example` to `.env` (or create `.env`)
176+
2. Set real credentials:
177+
178+
```bash
179+
MPKIT_URL=https://your-instance.platformos.com
180+
MPKIT_TOKEN=your-api-token
181+
MPKIT_EMAIL=your@email.com
182+
```
183+
184+
3. Tests will automatically load from `.env` via `dotenv/config`
185+
186+
## Configuration
187+
188+
### vitest.config.js
189+
190+
```javascript
191+
export default defineConfig({
192+
test: {
193+
environment: 'node',
194+
globals: true,
195+
include: ['test/**/*.{test,spec}.js'],
196+
setupFiles: ['./test/vitest-setup.js'],
197+
testTimeout: 10000,
198+
hookTimeout: 20000
199+
}
200+
});
201+
```
202+
203+
### Import Aliases
204+
205+
The project uses import aliases defined in `package.json`:
206+
207+
```javascript
208+
import something from '#lib/module.js'; // → ./lib/module.js
209+
import util from '#test/utils/file.js'; // → ./test/utils/file.js
210+
```
211+
212+
## Migration Progress
213+
214+
### Status: Active Development
215+
216+
Converting integration tests to have unit test equivalents with HTTP mocks using nock.
217+
218+
| Integration Test | Unit Test Status | Unit Test File | Notes |
219+
|-----------------|------------------|----------------|-------|
220+
| deploy.test.js | ✅ Done | `test/unit/deploy.test.js` | Gateway API mocked (push, getStatus, getInstance, sendManifest) |
221+
| sync.test.js | ✅ Partial | `test/unit/sync.test.js` | `shouldBeSynced` function fully tested |
222+
| modules-*.test.js | ✅ Done | `test/unit/modules.test.js` | Portal API mocked (jwtToken, moduleVersions, findModules, etc.) |
223+
| logs.test.js | ✅ Done | `test/unit/logs.test.js` | logs() and liquid() Gateway methods mocked |
224+
| gui-serve.test.js | 🔴 Pending | | May not need mocking (local server) |
225+
| test-run.test.js | 🔴 Pending | | Requires mocking test runner API |
226+
227+
### Unit Test Coverage Summary
228+
229+
New unit tests created with HTTP mocking:
230+
231+
- **`test/unit/deploy.test.js`** (14 tests)
232+
- Gateway API calls (push, getStatus, getInstance, sendManifest)
233+
- Error handling (401, 404, 500, network errors)
234+
- Presign URL functionality
235+
- Archive creation
236+
- Full deploy flow with mocks
237+
238+
- **`test/unit/modules.test.js`** (19 tests)
239+
- Portal.jwtToken() authentication
240+
- Portal.moduleVersions() version queries
241+
- Portal.findModules() module search
242+
- Portal.createVersion() version publishing
243+
- Device authorization flow
244+
- Module dependency resolution
245+
246+
- **`test/unit/logs.test.js`** (16 tests)
247+
- Gateway.logs() polling
248+
- Gateway.liquid() execution
249+
- Gateway.ping() health check
250+
- Error handling and log filtering
251+
252+
### Re-running Integration Tests
253+
254+
To validate unit test mocks against real API responses:
255+
256+
```bash
257+
# Run integration tests to verify real API behavior
258+
npm run test:integration
259+
260+
# Compare with unit tests
261+
npm run test:unit
262+
```
263+
264+
### Adding New Mocks
265+
266+
When adding tests for new API endpoints:
267+
268+
1. Run the integration test and capture real API responses
269+
2. Create mock responses in your unit test file
270+
3. Use nock to intercept the HTTP calls
271+
4. Verify the unit test behavior matches integration test
272+
273+
## Best Practices
274+
275+
1. **Prefer unit tests** - They're faster and more reliable
276+
2. **Integration tests for critical paths** - Deploy, sync, module operations
277+
3. **Mock at the HTTP level** - Use nock to intercept fetch calls
278+
4. **Keep mocks realistic** - Record from real API when possible
279+
5. **Clean up after tests** - Reset mocks and restore state
280+
6. **Use descriptive test names** - Document what's being tested
281+
282+
## Troubleshooting
283+
284+
### Tests failing with credential errors
285+
286+
- Unit tests: Make sure you're using mocks, not real API calls
287+
- Integration tests: Check your `.env` file has valid credentials
288+
289+
### Timeouts
290+
291+
- Unit tests should be fast (< 1 second). If timing out, you may be missing a mock.
292+
- Integration tests use extended timeouts. Increase if needed: `vi.setConfig({ testTimeout: 60000 })`
293+
294+
### Mock not matching
295+
296+
nock is strict about request matching. Debug with:
297+
298+
```javascript
299+
nock.recorder.rec(); // Record actual requests
300+
// Run your code
301+
nock.recorder.play(); // See what was called
302+
```

0 commit comments

Comments
 (0)