-
-
Notifications
You must be signed in to change notification settings - Fork 937
fix: build-state-manager null guards para checkpoints/failedAttempts/notifications #515
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,239 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
| * Testes para null guards em BuildStateManager | ||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||
| * Valida que getLastCheckpoint, getCheckpoint, getStatus, | ||||||||||||||||||||||||||||||||||||||||||||||
| * getNotifications, acknowledgeNotification e recordFailure | ||||||||||||||||||||||||||||||||||||||||||||||
| * não falham com TypeError quando arrays são undefined. | ||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||
| * Closes #513 | ||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+9
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const fs = require('fs'); | ||||||||||||||||||||||||||||||||||||||||||||||
| const path = require('path'); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Mock fs para evitar I/O real | ||||||||||||||||||||||||||||||||||||||||||||||
| jest.spyOn(fs, 'existsSync').mockReturnValue(false); | ||||||||||||||||||||||||||||||||||||||||||||||
| jest.spyOn(fs, 'mkdirSync').mockImplementation(() => {}); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const { BuildStateManager } = require('../../../.aios-core/core/execution/build-state-manager'); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| describe('BuildStateManager - null guards (#513)', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| let manager; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| beforeEach(() => { | ||||||||||||||||||||||||||||||||||||||||||||||
| manager = new BuildStateManager('test-story', { | ||||||||||||||||||||||||||||||||||||||||||||||
| rootPath: '/fake/project', | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================ | ||||||||||||||||||||||||||||||||||||||||||||||
| // getLastCheckpoint | ||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================ | ||||||||||||||||||||||||||||||||||||||||||||||
| describe('getLastCheckpoint', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| test('retorna null quando _state é null', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| manager._state = null; | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(manager.getLastCheckpoint()).toBeNull(); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| test('retorna null quando checkpoints é undefined', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| manager._state = { status: 'running' }; | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(manager.getLastCheckpoint()).toBeNull(); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| test('retorna null quando checkpoints é array vazio', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| manager._state = { checkpoints: [] }; | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(manager.getLastCheckpoint()).toBeNull(); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| test('retorna último checkpoint', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| const checkpoint = { id: 'cp-2', phase: 'build' }; | ||||||||||||||||||||||||||||||||||||||||||||||
| manager._state = { | ||||||||||||||||||||||||||||||||||||||||||||||
| checkpoints: [{ id: 'cp-1', phase: 'setup' }, checkpoint], | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(manager.getLastCheckpoint()).toEqual(checkpoint); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================ | ||||||||||||||||||||||||||||||||||||||||||||||
| // getCheckpoint | ||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================ | ||||||||||||||||||||||||||||||||||||||||||||||
| describe('getCheckpoint', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| test('retorna null quando _state é null', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| manager._state = null; | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(manager.getCheckpoint('cp-1')).toBeNull(); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| test('retorna null quando checkpoints é undefined', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| manager._state = { status: 'running' }; | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(manager.getCheckpoint('cp-1')).toBeNull(); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| test('retorna null quando checkpoint não existe', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| manager._state = { checkpoints: [{ id: 'cp-1' }] }; | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(manager.getCheckpoint('cp-999')).toBeNull(); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| test('retorna checkpoint pelo ID', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| const target = { id: 'cp-2', phase: 'test' }; | ||||||||||||||||||||||||||||||||||||||||||||||
| manager._state = { | ||||||||||||||||||||||||||||||||||||||||||||||
| checkpoints: [{ id: 'cp-1' }, target], | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(manager.getCheckpoint('cp-2')).toEqual(target); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================ | ||||||||||||||||||||||||||||||||||||||||||||||
| // constructor | ||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================ | ||||||||||||||||||||||||||||||||||||||||||||||
| describe('constructor', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| test('requer storyId', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(() => new BuildStateManager()).toThrow('storyId is required'); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| test('aceita storyId string', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| const m = new BuildStateManager('story-123'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(m.storyId).toBe('story-123'); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| test('inicializa _state como null', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(manager._state).toBeNull(); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| test('usa rootPath padrão se não fornecido', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| const m = new BuildStateManager('s1'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(m.rootPath).toBe(process.cwd()); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
+106
|
||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================ | |
| // constructor | |
| // ============================================================ | |
| describe('constructor', () => { | |
| test('requer storyId', () => { | |
| expect(() => new BuildStateManager()).toThrow('storyId is required'); | |
| }); | |
| test('aceita storyId string', () => { | |
| const m = new BuildStateManager('story-123'); | |
| expect(m.storyId).toBe('story-123'); | |
| }); | |
| test('inicializa _state como null', () => { | |
| expect(manager._state).toBeNull(); | |
| }); | |
| test('usa rootPath padrão se não fornecido', () => { | |
| const m = new BuildStateManager('s1'); | |
| expect(m.rootPath).toBe(process.cwd()); | |
| }); | |
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a discrepancy between the PR description and the actual code. The PR description states that checkpoints is optional in validation, but validateBuildState at line 103 includes 'checkpoints' in the required fields array. In reality, checkpoints is REQUIRED by validation, while failedAttempts and notifications are truly optional (not in the required array). The null guards being added here are still valuable for defensive programming and to handle cases where _state is manually set (as in tests), but the PR description should be corrected to accurately reflect that checkpoints is actually a required field according to the schema validation.