mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-17 12:16:59 +00:00
Merge pull request #1075 from kaitranntt/kai/chore/1071-ci-perf-budget
ci: add fast test budget warning
This commit is contained in:
@@ -95,6 +95,7 @@ jobs:
|
|||||||
needs: [build]
|
needs: [build]
|
||||||
env:
|
env:
|
||||||
BUN_INSTALL_CACHE_DIR: ${{ github.workspace }}/.bun/install/cache
|
BUN_INSTALL_CACHE_DIR: ${{ github.workspace }}/.bun/install/cache
|
||||||
|
FAST_TEST_BUDGET_SECONDS: '90'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -125,8 +126,39 @@ jobs:
|
|||||||
name: dist
|
name: dist
|
||||||
path: dist/
|
path: dist/
|
||||||
|
|
||||||
- name: Test
|
- name: Test fast bucket with perf budget
|
||||||
run: bun run test:all
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
start_epoch=$(date +%s)
|
||||||
|
test_status=0
|
||||||
|
if bun run test:fast; then
|
||||||
|
test_status=0
|
||||||
|
else
|
||||||
|
test_status=$?
|
||||||
|
fi
|
||||||
|
end_epoch=$(date +%s)
|
||||||
|
elapsed_seconds=$((end_epoch - start_epoch))
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "### test:fast perf budget"
|
||||||
|
echo ""
|
||||||
|
echo "- Elapsed: ${elapsed_seconds}s"
|
||||||
|
echo "- Budget: ${FAST_TEST_BUDGET_SECONDS}s"
|
||||||
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|
||||||
|
if (( elapsed_seconds > FAST_TEST_BUDGET_SECONDS )); then
|
||||||
|
echo "::warning::test:fast took ${elapsed_seconds}s (budget ${FAST_TEST_BUDGET_SECONDS}s) - check for undeclared slow tests in scripts/run-test-bucket.js"
|
||||||
|
echo "- Result: warning emitted. Check for undeclared slow tests in scripts/run-test-bucket.js." >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
else
|
||||||
|
echo "- Result: within budget." >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit "$test_status"
|
||||||
|
|
||||||
|
- name: Test slow bucket
|
||||||
|
run: bun run test:slow
|
||||||
|
|
||||||
- name: Test CLI e2e
|
- name: Test CLI e2e
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -256,12 +256,12 @@ describe('openai proxy daemon lifecycle', () => {
|
|||||||
}, 35000);
|
}, 35000);
|
||||||
|
|
||||||
it('fails when an explicit port is already occupied', async () => {
|
it('fails when an explicit port is already occupied', async () => {
|
||||||
const occupiedPort = await getPort();
|
|
||||||
const server = Bun.serve({
|
const server = Bun.serve({
|
||||||
port: occupiedPort,
|
port: 0,
|
||||||
hostname: '127.0.0.1',
|
hostname: '127.0.0.1',
|
||||||
fetch: () => new Response('busy'),
|
fetch: () => new Response('busy'),
|
||||||
});
|
});
|
||||||
|
const occupiedPort = server.port;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const settingsPath = path.join(tempDir, 'occupied-explicit.settings.json');
|
const settingsPath = path.join(tempDir, 'occupied-explicit.settings.json');
|
||||||
@@ -298,12 +298,12 @@ describe('openai proxy daemon lifecycle', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fails when a configured profile port is already occupied', async () => {
|
it('fails when a configured profile port is already occupied', async () => {
|
||||||
const occupiedPort = await getPort();
|
|
||||||
const server = Bun.serve({
|
const server = Bun.serve({
|
||||||
port: occupiedPort,
|
port: 0,
|
||||||
hostname: '127.0.0.1',
|
hostname: '127.0.0.1',
|
||||||
fetch: () => new Response('busy'),
|
fetch: () => new Response('busy'),
|
||||||
});
|
});
|
||||||
|
const occupiedPort = server.port;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mutateUnifiedConfig((config) => {
|
mutateUnifiedConfig((config) => {
|
||||||
@@ -347,12 +347,12 @@ describe('openai proxy daemon lifecycle', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('falls back when a configured shared proxy.port is occupied', async () => {
|
it('falls back when a configured shared proxy.port is occupied', async () => {
|
||||||
const occupiedPort = await getPort();
|
|
||||||
const server = Bun.serve({
|
const server = Bun.serve({
|
||||||
port: occupiedPort,
|
port: 0,
|
||||||
hostname: '127.0.0.1',
|
hostname: '127.0.0.1',
|
||||||
fetch: () => new Response('busy'),
|
fetch: () => new Response('busy'),
|
||||||
});
|
});
|
||||||
|
const occupiedPort = server.port;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mutateUnifiedConfig((config) => {
|
mutateUnifiedConfig((config) => {
|
||||||
@@ -396,12 +396,12 @@ describe('openai proxy daemon lifecycle', () => {
|
|||||||
|
|
||||||
it('keeps the existing proxy running if replacement startup fails', async () => {
|
it('keeps the existing proxy running if replacement startup fails', async () => {
|
||||||
const firstPort = await getPort();
|
const firstPort = await getPort();
|
||||||
const occupiedPort = await getPort();
|
|
||||||
const busyServer = Bun.serve({
|
const busyServer = Bun.serve({
|
||||||
port: occupiedPort,
|
port: 0,
|
||||||
hostname: '127.0.0.1',
|
hostname: '127.0.0.1',
|
||||||
fetch: () => new Response('busy'),
|
fetch: () => new Response('busy'),
|
||||||
});
|
});
|
||||||
|
const occupiedPort = busyServer.port;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const settingsPath = path.join(tempDir, 'rollback.settings.json');
|
const settingsPath = path.join(tempDir, 'rollback.settings.json');
|
||||||
|
|||||||
@@ -27,7 +27,14 @@ describe('push ci workflow', () => {
|
|||||||
expect(workflow).toContain("cmd: 'bun run lint'");
|
expect(workflow).toContain("cmd: 'bun run lint'");
|
||||||
expect(workflow).toContain("cmd: 'bun run format:check'");
|
expect(workflow).toContain("cmd: 'bun run format:check'");
|
||||||
expect(workflow).toContain('run: bun run build:all');
|
expect(workflow).toContain('run: bun run build:all');
|
||||||
expect(workflow).toContain('run: bun run test:all');
|
expect(workflow).toContain("FAST_TEST_BUDGET_SECONDS: '90'");
|
||||||
|
expect(workflow).toContain('name: Test fast bucket with perf budget');
|
||||||
|
expect(workflow).toContain('bun run test:fast');
|
||||||
|
expect(workflow).toContain('test_status=$?');
|
||||||
|
expect(workflow).toContain('exit "$test_status"');
|
||||||
|
expect(workflow).toContain('::warning::test:fast took ${elapsed_seconds}s');
|
||||||
|
expect(workflow).toContain('scripts/run-test-bucket.js');
|
||||||
|
expect(workflow).toContain('run: bun run test:slow');
|
||||||
expect(workflow).toContain("CCS_E2E_SKIP_BUILD: '1'");
|
expect(workflow).toContain("CCS_E2E_SKIP_BUILD: '1'");
|
||||||
expect(workflow).toContain('run: bun run test:e2e');
|
expect(workflow).toContain('run: bun run test:e2e');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ let stripAnthropicRoutingEnv: typeof import('../../../src/utils/shell-executor')
|
|||||||
let stripClaudeCodeEnv: typeof import('../../../src/utils/shell-executor').stripClaudeCodeEnv;
|
let stripClaudeCodeEnv: typeof import('../../../src/utils/shell-executor').stripClaudeCodeEnv;
|
||||||
let HeadlessExecutor: typeof import('../../../src/delegation/headless-executor').HeadlessExecutor;
|
let HeadlessExecutor: typeof import('../../../src/delegation/headless-executor').HeadlessExecutor;
|
||||||
let SharedManager: typeof import('../../../src/management/shared-manager').default;
|
let SharedManager: typeof import('../../../src/management/shared-manager').default;
|
||||||
|
let stopOpenAICompatProxy: typeof import('../../../src/proxy/proxy-daemon').stopOpenAICompatProxy;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
registerChildProcessMock();
|
registerChildProcessMock();
|
||||||
@@ -179,6 +180,9 @@ beforeAll(async () => {
|
|||||||
|
|
||||||
const headless = await import('../../../src/delegation/headless-executor');
|
const headless = await import('../../../src/delegation/headless-executor');
|
||||||
HeadlessExecutor = headless.HeadlessExecutor;
|
HeadlessExecutor = headless.HeadlessExecutor;
|
||||||
|
|
||||||
|
const proxyDaemon = await import('../../../src/proxy/proxy-daemon');
|
||||||
|
stopOpenAICompatProxy = proxyDaemon.stopOpenAICompatProxy;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
@@ -208,7 +212,14 @@ describe('CLAUDECODE environment stripping', () => {
|
|||||||
baselineSighupListeners = process.listeners('SIGHUP');
|
baselineSighupListeners = process.listeners('SIGHUP');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(async () => {
|
||||||
|
const tempCcsHome = process.env.CCS_HOME?.startsWith(os.tmpdir())
|
||||||
|
? process.env.CCS_HOME
|
||||||
|
: undefined;
|
||||||
|
if (tempCcsHome) {
|
||||||
|
await stopOpenAICompatProxy();
|
||||||
|
}
|
||||||
|
|
||||||
Object.defineProperty(process, 'platform', { value: originalPlatform });
|
Object.defineProperty(process, 'platform', { value: originalPlatform });
|
||||||
delete process.env.CLAUDECODE;
|
delete process.env.CLAUDECODE;
|
||||||
delete process.env.claudecode;
|
delete process.env.claudecode;
|
||||||
@@ -253,6 +264,10 @@ describe('CLAUDECODE environment stripping', () => {
|
|||||||
process.removeListener('SIGHUP', listener as (...args: unknown[]) => void);
|
process.removeListener('SIGHUP', listener as (...args: unknown[]) => void);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tempCcsHome) {
|
||||||
|
fs.rmSync(tempCcsHome, { recursive: true, force: true });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('stripClaudeCodeEnv removes CLAUDECODE case-insensitively', () => {
|
it('stripClaudeCodeEnv removes CLAUDECODE case-insensitively', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user