@@ -268,6 +268,13 @@ describe("missing @sentry/node package", () => {
268268 it ( "logs one warning and resolves" , async ( ) => {
269269 process . env [ BACKEND_DSN_ENV ] = "https://public@o0.ingest.sentry.io/1" ;
270270 const warn = vi . spyOn ( console , "warn" ) . mockImplementation ( ( ) => { } ) ;
271+ // Keep this failure-mode test valid when the optional real-SDK tests run.
272+ vi . doMock ( "../peer-version-check.js" , ( ) => ( {
273+ checkExactPeerVersions : ( ) => ( {
274+ ok : false ,
275+ detail : { missing : [ "@sentry/node" ] , mismatched : [ ] } ,
276+ } ) ,
277+ } ) ) ;
271278
272279 const { sentryReady } = await importFreshSentry ( ) ;
273280
@@ -537,6 +544,41 @@ describe("buildSentryInitOptions serverName", () => {
537544 } ) ;
538545} ) ;
539546
547+ describe ( "buildSentryInitOptions release" , ( ) => {
548+ const commit = "0123456789abcdef0123456789abcdef01234567" ;
549+ const integrations = {
550+ httpIntegration : ( ) => ( { name : "Http" } ) ,
551+ onUnhandledRejectionIntegration : ( ) => ( { name : "OnUnhandledRejection" } ) ,
552+ } ;
553+
554+ beforeEach ( ( ) => {
555+ vi . stubEnv ( "SENTRY_RELEASE" , "" ) ;
556+ vi . doMock ( "../build-commit.js" , ( ) => ( { readBuildCommit : ( ) => commit } ) ) ;
557+ } ) ;
558+
559+ afterEach ( ( ) => {
560+ vi . unstubAllEnvs ( ) ;
561+ vi . doUnmock ( "../build-commit.js" ) ;
562+ } ) ;
563+
564+ it ( "uses the server build commit" , async ( ) => {
565+ const { buildSentryInitOptions } = await importFreshSentry ( ) ;
566+ expect ( buildSentryInitOptions ( "test-dsn" , integrations ) . release ) . toBe ( commit ) ;
567+ } ) ;
568+
569+ it ( "preserves an operator's explicit release" , async ( ) => {
570+ vi . stubEnv ( "SENTRY_RELEASE" , " custom-release " ) ;
571+ const { buildSentryInitOptions } = await importFreshSentry ( ) ;
572+ expect ( buildSentryInitOptions ( "test-dsn" , integrations ) . release ) . toBe ( "custom-release" ) ;
573+ } ) ;
574+
575+ it ( "leaves an unknown build unattributed" , async ( ) => {
576+ vi . doMock ( "../build-commit.js" , ( ) => ( { readBuildCommit : ( ) => null } ) ) ;
577+ const { buildSentryInitOptions } = await importFreshSentry ( ) ;
578+ expect ( buildSentryInitOptions ( "test-dsn" , integrations ) . release ) . toBeUndefined ( ) ;
579+ } ) ;
580+ } ) ;
581+
540582describe ( "with @sentry/node mocked" , ( ) => {
541583 it ( "initializes the client and shares captureException / shutdownSentry with it" , async ( ) => {
542584 process . env [ DSN_ENV ] = "https://public@o0.ingest.sentry.io/1" ;
@@ -644,6 +686,22 @@ describe.skipIf(!sentryPackage)("captured event shape against the real @sentry/n
644686 Sentry . init ( options ) ;
645687 }
646688
689+ it ( "attaches the actual build commit to an emitted event" , async ( ) => {
690+ const commit = "0123456789abcdef0123456789abcdef01234567" ;
691+ vi . stubEnv ( "PAPERCLIP_BUILD_COMMIT" , commit ) ;
692+ vi . stubEnv ( "SENTRY_RELEASE" , "" ) ;
693+ try {
694+ let captured : Record < string , unknown > | null = null ;
695+ await initRealSentryForTest ( ( event ) => { captured = event ; } ) ;
696+ sentryPackage ! . captureException ( new Error ( "build attribution check" ) ) ;
697+ await sentryPackage ! . flush ( 2000 ) ;
698+ expect ( captured ) . toMatchObject ( { release : commit } ) ;
699+ expect ( captured ) . not . toHaveProperty ( "request" ) ;
700+ } finally {
701+ vi . unstubAllEnvs ( ) ;
702+ }
703+ } ) ;
704+
647705 it ( "a server event captured after a console.error call carries no console breadcrumb" , async ( ) => {
648706 const Sentry = sentryPackage ! ;
649707 let captured : Record < string , unknown > | null = null ;
0 commit comments