Skip to content

Commit 73945f9

Browse files
xxshubhamxxclaude
andcommitted
test(observability): add BuildUpdate one-shot regression test [SDK-6574]
Drives multiple test events through a single reporter instance (deps stubbed via proxyquire) and asserts BuildUpdate is uploaded at most once. Fails on the buggy code (one BuildUpdate per event) and passes on the fix. Also asserts BuildUpdate is deferred until the framework version is known. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 1e08d42 commit 73945f9

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

  • test/unit/bin/testObservability/reporter
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
'use strict';
2+
3+
/*
4+
* Regression test for SDK-6574.
5+
*
6+
* The Observability reporter is meant to emit `BuildUpdate` once. A typo made
7+
* the one-shot guard set a dead sibling field (`shouldSendBuildUpdate`) instead
8+
* of `haveSentBuildUpdate`, so the flag never flipped and `BuildUpdate` re-fired
9+
* on every test/hook event. This test drives several events through a single
10+
* reporter instance and asserts `BuildUpdate` is uploaded at most once.
11+
*
12+
* Pre-fix this test FAILS (one BuildUpdate per event); post-fix it PASSES.
13+
*/
14+
15+
const chai = require('chai');
16+
const chaiAsPromised = require('chai-as-promised');
17+
const sinon = require('sinon');
18+
const proxyquire = require('proxyquire').noCallThru();
19+
const { EventEmitter } = require('events');
20+
21+
const { expect } = chai;
22+
chai.use(chaiAsPromised);
23+
24+
const REPORTER_PATH = '../../../../../bin/testObservability/reporter/index.js';
25+
26+
// Minimal Cypress/Mocha "test" object — just enough to drive sendTestRunEvent('TestRunStarted').
27+
function makeTest(id) {
28+
return {
29+
body: 'function () {}',
30+
title: 'spec ' + id,
31+
testAnalyticsId: 'test-' + id,
32+
retryOf: null,
33+
duration: 10,
34+
file: 'cypress/e2e/spec.cy.js',
35+
test_started_at: new Date().toISOString(),
36+
started_at: new Date().toISOString(),
37+
fullTitle: () => 'suite spec ' + id,
38+
titlePath: () => ['suite', 'spec ' + id],
39+
};
40+
}
41+
42+
describe('testObservability reporter — BuildUpdate one-shot guard [SDK-6574]', () => {
43+
let uploadEventData;
44+
let MyReporter;
45+
let savedEnv;
46+
47+
const buildUpdateCount = () =>
48+
uploadEventData.getCalls().filter(c => c.args[0] && c.args[0].event_type === 'BuildUpdate').length;
49+
50+
beforeEach(() => {
51+
savedEnv = { ...process.env };
52+
delete process.env.observability_framework_version;
53+
delete process.env.observability_integration;
54+
delete process.env.observability_product;
55+
process.env.REPORTER_API_PORT_NO = '0';
56+
57+
uploadEventData = sinon.spy(async () => ({ status: 'ok' }));
58+
59+
const helperStub = {
60+
requireModule: (m) => require(m), // load the REAL mocha (Base/utils/Mocha) as the source expects
61+
uploadEventData,
62+
failureData: () => [],
63+
PathHelper: class { prefixTestPath(p) { return p; } },
64+
getTestEnv: () => ({ location_prefix: '' }),
65+
getHookDetails: () => ['before all', 'hook'],
66+
getHooksForTest: () => [],
67+
mapTestHooks: () => {},
68+
debug: () => {},
69+
isBrowserstackInfra: () => true,
70+
getHookSkippedTests: () => [],
71+
getOSDetailsFromSystem: async () => ({ os: 'OSX', os_version: '13' }),
72+
findGitConfig: () => null,
73+
getFileSeparatorData: () => '/',
74+
setCrashReportingConfigFromReporter: () => {},
75+
debugOnConsole: () => {},
76+
};
77+
78+
const fakeHttpServer = { on() { return this; }, listen() { return this; }, close() {} };
79+
80+
MyReporter = proxyquire(REPORTER_PATH, {
81+
'../helper/helper': helperStub,
82+
'../helper/constants': { IPC_EVENTS: {}, TEST_REPORTING_ANALYTICS: 'TRA', consoleHolder: console },
83+
'../plugin/ipcServer': { startIPCServer: () => {} },
84+
'../../testhub/utils': { shouldProcessEventForTesthub: () => true },
85+
'http': { createServer: () => fakeHttpServer },
86+
});
87+
});
88+
89+
afterEach(() => {
90+
sinon.restore();
91+
process.env = savedEnv;
92+
});
93+
94+
function newReporter() {
95+
const runner = new EventEmitter();
96+
const reporter = new MyReporter(runner, { reporterOptions: {} });
97+
if (reporter.httpServer && reporter.httpServer.close) reporter.httpServer.close();
98+
reporter.currentCypressVersion = '13.6.0'; // framework version known (arrives via IPC in prod)
99+
return reporter;
100+
}
101+
102+
it('sends BuildUpdate exactly once across many test events (regression: was once-per-event)', async () => {
103+
const reporter = newReporter();
104+
for (let i = 0; i < 5; i++) {
105+
await reporter.sendTestRunEvent(makeTest(i), undefined, false, 'TestRunStarted');
106+
}
107+
expect(buildUpdateCount()).to.equal(1);
108+
expect(reporter.haveSentBuildUpdate).to.equal(true);
109+
});
110+
111+
it('does not send BuildUpdate until the framework version is known', async () => {
112+
const reporter = newReporter();
113+
reporter.currentCypressVersion = undefined; // no version yet, no env override
114+
await reporter.sendTestRunEvent(makeTest('x'), undefined, false, 'TestRunStarted');
115+
expect(buildUpdateCount()).to.equal(0);
116+
expect(reporter.haveSentBuildUpdate).to.equal(false);
117+
});
118+
});

0 commit comments

Comments
 (0)