feat: add .spec.ts generation for models, mappers, repositories and use-cases

Add 4 new Mustache templates for generating unit test specs:
- model-entity.spec.mustache: tests instantiation, property setting, mock builder
- mapper.spec.mustache: tests per-property DTO→Entity mapping, instanceof, all fields
- api.repository.impl.spec.mustache: tests HTTP method, response mapping, error propagation
- api.use-cases.impl.spec.mustache: tests repository delegation, observable forwarding

Generator changes:
- Add uppercaseHttpMethod to TagOperation for spec HTTP assertions
- Add testValue to TagOperationParam for auto-generated test arguments
- Add resolveTestParamValue utility for primitive/complex type test literals
- Add specs counter to GeneratedCount and GenerationReport
- Wire 4 new renderTemplate calls in schema and tag loops
- Update report generator to count .spec.ts files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
didavila
2026-03-26 10:52:58 +01:00
parent 05a58c4254
commit 463626da0c
8 changed files with 343 additions and 5 deletions

View File

@@ -0,0 +1,97 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { {{classname}}RepositoryImpl } from './{{classFilename}}.repository.impl';
{{#returnImports}}
import { mock{{classname}}Dto } from '@/dtos/{{classFilename}}.dto.mock';
import { mock{{classname}}Model } from '@/entities/models/{{classFilename}}.model.mock';
{{/returnImports}}
describe('{{classname}}RepositoryImpl', () => {
let repository: {{classname}}RepositoryImpl;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [{{classname}}RepositoryImpl]
});
repository = TestBed.inject({{classname}}RepositoryImpl);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
});
it('should be created', () => {
expect(repository).toBeTruthy();
});
{{#operation}}
describe('{{nickname}}', () => {
it('should perform a {{uppercaseHttpMethod}} request', () => {
repository.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe();
const req = httpMock.expectOne((r) => r.method === '{{uppercaseHttpMethod}}');
expect(req.request.method).toBe('{{uppercaseHttpMethod}}');
{{#isListContainer}}
req.flush({ items: [mock{{returnBaseType}}Dto()] });
{{/isListContainer}}
{{^isListContainer}}
{{#returnType}}
req.flush(mock{{returnBaseType}}Dto());
{{/returnType}}
{{^returnType}}
req.flush(null);
{{/returnType}}
{{/isListContainer}}
});
{{#returnType}}
it('should map the response to the domain model', () => {
const dto = mock{{returnBaseType}}Dto();
const expectedModel = mock{{returnBaseType}}Model();
{{#isListContainer}}
repository.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe((result) => {
expect(result).toBeTruthy();
expect(result.length).toBeGreaterThan(0);
});
httpMock.expectOne((r) => r.method === '{{uppercaseHttpMethod}}').flush({ items: [dto] });
{{/isListContainer}}
{{^isListContainer}}
repository.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe((result) => {
expect(result).toEqual(expectedModel);
});
httpMock.expectOne((r) => r.method === '{{uppercaseHttpMethod}}').flush(dto);
{{/isListContainer}}
});
{{/returnType}}
it('should propagate HTTP errors', (done) => {
repository.{{nickname}}({{#allParams}}{{{testValue}}}{{^-last}}, {{/-last}}{{/allParams}}).subscribe({
error: (err) => {
expect(err.status).toBe(500);
done();
}
});
httpMock
.expectOne((r) => r.method === '{{uppercaseHttpMethod}}')
.flush('Internal Server Error', { status: 500, statusText: 'Internal Server Error' });
});
});
{{/operation}}
});
{{/operations}}
{{/apis}}
{{/apiInfo}}