feat: dead-config sweep, test suite foundation, widget settingsSchema validation
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Sprint G: audited every BootstrapConfig field for a real runtime consumer (docs/DEAD-CONFIG-AUDIT.md). Wired 3 previously-dead editable fields: footer.logoUrl, company.address.street/contacts.phone, catalog.suggestionsEnabled. Remaining dead fields needing a business/design decision tracked in PRODUCT_BACKLOG.md/KNOWN-ISSUES.md, not silently left. Sprint H: 6 new spec files (test count 57 -> 83), covering ProjectEditorFacade (undo/redo, draft persistence, publish gating), AdminAnalyticsFacade (never-fabricate-a-number contract), and regression coverage for this session's carousel/hero/profile-toggle fixes. Sprint I: widget settingsSchema (declared in widget-manifest.json, never validated) now enforced via a new lightweight schema check in ProjectValidator, surfaced through the existing issuesByField pipeline. Same check reused in diagnostics so editor and diagnostics can't disagree. Verification: tsc clean, ng build clean, 83/83 tests pass, barry-cache validate clean (2 pre-existing unrelated warnings only). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { AdminAnalyticsFacade } from './admin-analytics.facade';
|
||||
import { AdminOrdersLocalGateway } from '../../orders/services/admin-orders-local.gateway';
|
||||
import { AdminProductsLocalGateway } from '../../products/services/admin-products-local.gateway';
|
||||
import { ADMIN_CATEGORIES_GATEWAY } from '../../categories/services/admin-categories-gateway.token';
|
||||
import { AdminModerationLocalGateway } from '../../moderation/services/admin-moderation-local.gateway';
|
||||
import { AdminDashboardFacade } from '../../dashboard/facade/admin-dashboard.facade';
|
||||
import { AdminOrder } from '../../orders/models/admin-order.model';
|
||||
|
||||
function makeOrder(overrides: Partial<AdminOrder> = {}): AdminOrder {
|
||||
return {
|
||||
id: 'order-1',
|
||||
createdAt: new Date().toISOString(),
|
||||
status: 'completed',
|
||||
total: 100,
|
||||
currency: 'RUB',
|
||||
customer: { email: 'buyer@example.com' },
|
||||
items: [{ productId: 'p1', name: 'Widget', quantity: 1, price: 100 }],
|
||||
...overrides,
|
||||
} as unknown as AdminOrder;
|
||||
}
|
||||
|
||||
describe('AdminAnalyticsFacade (never-fabricate-a-number contract)', () => {
|
||||
let facade: AdminAnalyticsFacade;
|
||||
let ordersGateway: jasmine.SpyObj<AdminOrdersLocalGateway>;
|
||||
let productsGateway: jasmine.SpyObj<AdminProductsLocalGateway>;
|
||||
let categoriesGateway: jasmine.SpyObj<{ loadCategories: () => unknown }>;
|
||||
let moderationGateway: jasmine.SpyObj<AdminModerationLocalGateway>;
|
||||
let dashboardFacade: jasmine.SpyObj<AdminDashboardFacade>;
|
||||
|
||||
function configure(bootstrapPresent: boolean): void {
|
||||
ordersGateway = jasmine.createSpyObj('AdminOrdersLocalGateway', ['loadOrders']);
|
||||
productsGateway = jasmine.createSpyObj('AdminProductsLocalGateway', ['loadProducts']);
|
||||
categoriesGateway = jasmine.createSpyObj('ADMIN_CATEGORIES_GATEWAY', ['loadCategories']);
|
||||
moderationGateway = jasmine.createSpyObj('AdminModerationLocalGateway', ['loadReviews']);
|
||||
dashboardFacade = jasmine.createSpyObj('AdminDashboardFacade', [
|
||||
'ensureLoaded', 'activityEntries', 'bootstrap', 'validationIssues', 'enabledWidgetsCount', 'staticPagesUnpublishedCount',
|
||||
]);
|
||||
|
||||
ordersGateway.loadOrders.and.returnValue(of({ items: [makeOrder()], total: 1 } as any));
|
||||
productsGateway.loadProducts.and.returnValue(of({ items: [], total: 0 } as any));
|
||||
categoriesGateway.loadCategories.and.returnValue(of([]));
|
||||
moderationGateway.loadReviews.and.returnValue(of({ items: [], total: 0 } as any));
|
||||
dashboardFacade.activityEntries.and.returnValue([]);
|
||||
dashboardFacade.bootstrap.and.returnValue(bootstrapPresent ? ({ schemaVersion: '1', tenant: { id: 't1' } } as any) : null);
|
||||
dashboardFacade.validationIssues.and.returnValue([]);
|
||||
dashboardFacade.enabledWidgetsCount.and.returnValue(0);
|
||||
dashboardFacade.staticPagesUnpublishedCount.and.returnValue(0);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: AdminOrdersLocalGateway, useValue: ordersGateway },
|
||||
{ provide: AdminProductsLocalGateway, useValue: productsGateway },
|
||||
{ provide: ADMIN_CATEGORIES_GATEWAY, useValue: categoriesGateway },
|
||||
{ provide: AdminModerationLocalGateway, useValue: moderationGateway },
|
||||
{ provide: AdminDashboardFacade, useValue: dashboardFacade },
|
||||
],
|
||||
});
|
||||
|
||||
facade = TestBed.inject(AdminAnalyticsFacade);
|
||||
}
|
||||
|
||||
it('never fabricates conversionRate - stays null even with real order data', () => {
|
||||
configure(true);
|
||||
facade.load();
|
||||
|
||||
expect(facade.summary()?.conversionRate).toBeNull();
|
||||
expect(facade.summary()?.revenueTotal).toBe(100);
|
||||
expect(facade.summary()?.ordersCount).toBe(1);
|
||||
});
|
||||
|
||||
it('never fabricates the "performance" health check - always unknown (no real data source)', () => {
|
||||
configure(true);
|
||||
facade.load();
|
||||
|
||||
const performance = facade.marketplaceHealth().find(check => check.code === 'performance');
|
||||
expect(performance?.status).toBe('unknown');
|
||||
});
|
||||
|
||||
it('reports backend-connectivity and homepage-configured as unknown when bootstrap has not loaded, instead of guessing', () => {
|
||||
configure(false);
|
||||
facade.load();
|
||||
|
||||
const backend = facade.marketplaceHealth().find(check => check.code === 'backend-connectivity');
|
||||
const homepage = facade.marketplaceHealth().find(check => check.code === 'homepage-configured');
|
||||
expect(backend?.status).toBe('unknown');
|
||||
expect(homepage?.status).toBe('unknown');
|
||||
});
|
||||
|
||||
it('reports backend-connectivity as healthy only once bootstrap is actually present', () => {
|
||||
configure(true);
|
||||
facade.load();
|
||||
|
||||
const backend = facade.marketplaceHealth().find(check => check.code === 'backend-connectivity');
|
||||
expect(backend?.status).toBe('healthy');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user