@@ -14,15 +14,32 @@ import { buildServer } from "../server";
1414import { getServerContext } from "../test-setup" ;
1515import findProjects from "../tools/catalog/find-projects" ;
1616import type { ServerContext } from "../types" ;
17+ import { type Target , setTargetTagsAndAttributes } from "./scope" ;
1718
1819const beforeSendSpan = vi . fn ( ( span : StreamedSpanJSON ) => span ) ;
1920const beforeSend = vi . fn ( ( event : ErrorEvent ) => event ) ;
2021let sentry : ServerRuntimeClient ;
2122
23+ // Without an async context strategy, withIsolationScope does not fork the
24+ // isolation scope, so values set by one test stay there for the next one.
25+ function resetScopeContext ( ) : void {
26+ const scope = getIsolationScope ( ) ;
27+ for ( const key of [
28+ "organization.slug" ,
29+ "project.slug" ,
30+ "project.id" ,
31+ "team.slug" ,
32+ "issue.id" ,
33+ "client.id" ,
34+ ] ) {
35+ scope . setAttribute ( key , undefined ) ;
36+ scope . setTag ( key , undefined ) ;
37+ }
38+ }
39+
2240beforeEach ( ( ) => {
2341 vi . clearAllMocks ( ) ;
24- getIsolationScope ( ) . setAttribute ( "organization.slug" , undefined ) ;
25- getIsolationScope ( ) . setTag ( "organization.slug" , undefined ) ;
42+ resetScopeContext ( ) ;
2643 sentry = new ServerRuntimeClient ( {
2744 dsn : "https://public@example.com/1" ,
2845 integrations : [ ] ,
@@ -42,8 +59,7 @@ beforeEach(() => {
4259afterEach ( async ( ) => {
4360 await sentry . close ( ) ;
4461 getCurrentScope ( ) . setClient ( undefined ) ;
45- getIsolationScope ( ) . setAttribute ( "organization.slug" , undefined ) ;
46- getIsolationScope ( ) . setTag ( "organization.slug" , undefined ) ;
62+ resetScopeContext ( ) ;
4763} ) ;
4864
4965async function callTool (
@@ -165,3 +181,60 @@ describe("request and tool telemetry", () => {
165181 ) ;
166182 } ) ;
167183} ) ;
184+
185+ describe ( "setTargetTagsAndAttributes" , ( ) => {
186+ function scopeValuesFor ( target : Target ) {
187+ setTargetTagsAndAttributes ( target ) ;
188+ const { tags, attributes } = getIsolationScope ( ) . getScopeData ( ) ;
189+ return { tags, attributes } ;
190+ }
191+
192+ it ( "sets organization, project, and team as tags and attributes" , ( ) => {
193+ const { tags, attributes } = scopeValuesFor ( {
194+ organizationSlug : "sentry" ,
195+ projectSlug : "javascript" ,
196+ projectId : 42 ,
197+ teamSlug : "sdk" ,
198+ } ) ;
199+ const expected = {
200+ "organization.slug" : "sentry" ,
201+ "project.slug" : "javascript" ,
202+ "project.id" : "42" ,
203+ "team.slug" : "sdk" ,
204+ } ;
205+ expect ( tags ) . toMatchObject ( expected ) ;
206+ expect ( attributes ) . toMatchObject ( expected ) ;
207+ } ) ;
208+
209+ it . each ( [
210+ { projectSlugOrId : "42" , key : "project.id" , other : "project.slug" } ,
211+ { projectSlugOrId : "javascript" , key : "project.slug" , other : "project.id" } ,
212+ ] ) (
213+ "maps projectSlugOrId $projectSlugOrId to $key" ,
214+ ( { projectSlugOrId, key, other } ) => {
215+ const { tags, attributes } = scopeValuesFor ( {
216+ organizationSlug : "sentry" ,
217+ projectSlugOrId,
218+ } ) ;
219+ expect ( tags [ key ] ) . toBe ( projectSlugOrId ) ;
220+ expect ( attributes [ key ] ) . toBe ( projectSlugOrId ) ;
221+ expect ( tags [ other ] ) . toBeUndefined ( ) ;
222+ expect ( attributes [ other ] ) . toBeUndefined ( ) ;
223+ } ,
224+ ) ;
225+
226+ it ( "skips empty project and team values" , ( ) => {
227+ const { tags, attributes } = scopeValuesFor ( {
228+ organizationSlug : "sentry" ,
229+ projectSlug : null ,
230+ projectId : undefined ,
231+ projectSlugOrId : "" ,
232+ teamSlug : null ,
233+ } ) ;
234+ for ( const key of [ "project.slug" , "project.id" , "team.slug" ] ) {
235+ expect ( tags [ key ] ) . toBeUndefined ( ) ;
236+ expect ( attributes [ key ] ) . toBeUndefined ( ) ;
237+ }
238+ expect ( attributes [ "organization.slug" ] ) . toBe ( "sentry" ) ;
239+ } ) ;
240+ } ) ;
0 commit comments