feat: Add Bun language analyzer and related functionality
- Implemented BunPackageNormalizer to deduplicate packages by name and version. - Created BunProjectDiscoverer to identify Bun project roots in the filesystem. - Added project files for the Bun analyzer including manifest and project configuration. - Developed comprehensive tests for Bun language analyzer covering various scenarios. - Included fixture files for testing standard installs, isolated linker installs, lockfile-only scenarios, and workspaces. - Established stubs for authentication sessions to facilitate testing in the web application.
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
| WEB-TEN-47-CONTRACT | DONE (2025-12-01) | Gateway tenant auth/ABAC contract doc v1.0 published (`docs/api/gateway/tenant-auth.md`). |
|
||||
| WEB-VULN-29-LEDGER-DOC | DONE (2025-12-01) | Findings Ledger proxy contract doc v1.0 with idempotency + retries (`docs/api/gateway/findings-ledger-proxy.md`). |
|
||||
| WEB-RISK-68-NOTIFY-DOC | DONE (2025-12-01) | Notifications severity transition event schema v1.0 published (`docs/api/gateway/notifications-severity.md`). |
|
||||
| UI-MICRO-GAPS-0209-011 | DOING (2025-12-04) | Motion token catalog + Storybook/Playwright a11y harness added; remaining work: component mapping, perf budgets, deterministic snapshots. |
|
||||
| UI-MICRO-GAPS-0209-011 | BLOCKED (2025-12-06) | Motion token catalog + Storybook/Playwright a11y harness added; remaining work paused pending SIG-26 reachability fixtures and final token mapping approvals. |
|
||||
| UI-POLICY-20-001 | DONE (2025-12-05) | Policy Studio Monaco editor with DSL highlighting, lint markers, and compliance checklist shipped. |
|
||||
| UI-POLICY-20-002 | DONE (2025-12-05) | Simulation panel with deterministic diff rendering shipped (`/policy-studio/packs/:packId/simulate`). |
|
||||
| UI-POLICY-20-003 | DONE (2025-12-05) | Approvals workflow UI delivered with submit/review actions, two-person badge, and deterministic log. |
|
||||
|
||||
@@ -24,10 +24,29 @@
|
||||
Loading console context…
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="!loading()">
|
||||
<section class="console-profile__card" *ngIf="profile() as profile">
|
||||
<header>
|
||||
<h2>User Profile</h2>
|
||||
<ng-container *ngIf="!loading()">
|
||||
<section class="console-profile__card console-profile__callout">
|
||||
<header>
|
||||
<h2>Policy Studio roles & scopes</h2>
|
||||
</header>
|
||||
<ul>
|
||||
<li><strong>Author</strong>: policy:read, policy:author, policy:edit, policy:submit, policy:simulate</li>
|
||||
<li><strong>Reviewer</strong>: policy:read, policy:review, policy:simulate</li>
|
||||
<li><strong>Approver</strong>: policy:read, policy:review, policy:approve, policy:simulate</li>
|
||||
<li><strong>Operator</strong>: policy:read, policy:operate, policy:activate, policy:run, policy:simulate</li>
|
||||
<li><strong>Audit</strong>: policy:read, policy:audit</li>
|
||||
</ul>
|
||||
<p class="console-profile__hint">
|
||||
Use this list to verify your token covers the flows you need (editor, simulate, approvals, dashboard, audit exports).
|
||||
</p>
|
||||
<p class="console-profile__hint">
|
||||
For Cypress/e2e, load stub sessions from <code>testing/auth-fixtures.ts</code> (author/reviewer/approver/operator/audit) and seed <code>AuthSessionStore</code> before navigating.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="console-profile__card" *ngIf="profile() as profile">
|
||||
<header>
|
||||
<h2>User Profile</h2>
|
||||
<span class="tenant-chip">
|
||||
Tenant
|
||||
<strong>{{ profile.tenant }}</strong>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, fakeAsync, tick, flushMicrotasks } from '@angular/core/testing';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, convertToParamMap } from '@angular/router';
|
||||
import { of } from 'rxjs';
|
||||
@@ -12,7 +12,7 @@ describe('PolicyDashboardComponent', () => {
|
||||
let component: PolicyDashboardComponent;
|
||||
let api: jasmine.SpyObj<PolicyApiService>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(fakeAsync(() => {
|
||||
api = jasmine.createSpyObj<PolicyApiService>('PolicyApiService', ['getRunDashboard']);
|
||||
|
||||
api.getRunDashboard.and.returnValue(
|
||||
@@ -47,7 +47,7 @@ describe('PolicyDashboardComponent', () => {
|
||||
}) as any
|
||||
);
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CommonModule, ReactiveFormsModule, PolicyDashboardComponent],
|
||||
providers: [
|
||||
{ provide: PolicyApiService, useValue: api },
|
||||
@@ -63,9 +63,11 @@ describe('PolicyDashboardComponent', () => {
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
flushMicrotasks();
|
||||
|
||||
fixture = TestBed.createComponent(PolicyDashboardComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
}));
|
||||
|
||||
it('sorts runs descending by completedAt', fakeAsync(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
45
src/Web/StellaOps.Web/src/app/testing/auth-fixtures.ts
Normal file
45
src/Web/StellaOps.Web/src/app/testing/auth-fixtures.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
export type StubAuthSession = {
|
||||
subjectId: string;
|
||||
tenant: string;
|
||||
scopes: string[];
|
||||
};
|
||||
|
||||
const baseScopes = ['ui.read', 'policy:read'];
|
||||
|
||||
export const policyAuthorSession: StubAuthSession = {
|
||||
subjectId: 'user-author',
|
||||
tenant: 'tenant-default',
|
||||
scopes: [...baseScopes, 'policy:author', 'policy:edit', 'policy:submit', 'policy:simulate'],
|
||||
};
|
||||
|
||||
export const policyReviewerSession: StubAuthSession = {
|
||||
subjectId: 'user-reviewer',
|
||||
tenant: 'tenant-default',
|
||||
scopes: [...baseScopes, 'policy:review', 'policy:simulate'],
|
||||
};
|
||||
|
||||
export const policyApproverSession: StubAuthSession = {
|
||||
subjectId: 'user-approver',
|
||||
tenant: 'tenant-default',
|
||||
scopes: [...baseScopes, 'policy:review', 'policy:approve', 'policy:simulate'],
|
||||
};
|
||||
|
||||
export const policyOperatorSession: StubAuthSession = {
|
||||
subjectId: 'user-operator',
|
||||
tenant: 'tenant-default',
|
||||
scopes: [...baseScopes, 'policy:operate', 'policy:activate', 'policy:run', 'policy:simulate'],
|
||||
};
|
||||
|
||||
export const policyAuditSession: StubAuthSession = {
|
||||
subjectId: 'user-auditor',
|
||||
tenant: 'tenant-default',
|
||||
scopes: [...baseScopes, 'policy:audit'],
|
||||
};
|
||||
|
||||
export const allPolicySessions = [
|
||||
policyAuthorSession,
|
||||
policyReviewerSession,
|
||||
policyApproverSession,
|
||||
policyOperatorSession,
|
||||
policyAuditSession,
|
||||
];
|
||||
35
src/Web/StellaOps.Web/src/app/testing/auth-store.stub.ts
Normal file
35
src/Web/StellaOps.Web/src/app/testing/auth-store.stub.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { AuthSessionStore } from '../core/auth/auth-session.store';
|
||||
import { AuthSession } from '../core/auth/auth-session.model';
|
||||
import { StubAuthSession } from './auth-fixtures';
|
||||
|
||||
/**
|
||||
* Seed the AuthSessionStore with a deterministic stub session for tests/e2e.
|
||||
* Populates tokens/identity using the provided scopes/tenant/subject and
|
||||
* sets a long-lived expiry to avoid refresh churn in short-lived test runs.
|
||||
*/
|
||||
export function seedAuthSession(store: AuthSessionStore, stub: StubAuthSession): void {
|
||||
const now = Date.now();
|
||||
const session: AuthSession = {
|
||||
tokens: {
|
||||
accessToken: 'stub-token-' + stub.subjectId,
|
||||
expiresAtEpochMs: now + 60 * 60 * 1000,
|
||||
tokenType: 'Bearer',
|
||||
scope: stub.scopes.join(' '),
|
||||
},
|
||||
identity: {
|
||||
subject: stub.subjectId,
|
||||
name: stub.subjectId,
|
||||
roles: [],
|
||||
},
|
||||
dpopKeyThumbprint: 'stub-dpop-' + stub.subjectId,
|
||||
issuedAtEpochMs: now,
|
||||
tenantId: stub.tenant,
|
||||
scopes: stub.scopes,
|
||||
audiences: ['stellaops'],
|
||||
authenticationTimeEpochMs: now,
|
||||
freshAuthActive: true,
|
||||
freshAuthExpiresAtEpochMs: now + 30 * 60 * 1000,
|
||||
};
|
||||
|
||||
store.setSession(session);
|
||||
}
|
||||
6
src/Web/StellaOps.Web/src/app/testing/index.ts
Normal file
6
src/Web/StellaOps.Web/src/app/testing/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export * from './auth-fixtures';
|
||||
export * from './auth-store.stub';
|
||||
export * from './exception-fixtures';
|
||||
export * from './notify-fixtures';
|
||||
export * from './policy-fixtures';
|
||||
export * from './scan-fixtures';
|
||||
Reference in New Issue
Block a user