@@ -36,6 +36,10 @@ const paddedReadableLabelArbitrary = fc.tuple(
3636 fc . constantFrom ( "" , " " , " " )
3737) . map ( ( [ left , value , right ] ) => `${ left } ${ value } ${ right } ` )
3838
39+ const blankLabelArbitrary = fc . constantFrom ( "" , " " , " " )
40+
41+ const emptyOrMainRefArbitrary = fc . constantFrom ( "" , " " , " " , "main" )
42+
3943const repositoryArbitrary = fc . record ( {
4044 owner : gitHubPathSegmentArbitrary ,
4145 repo : gitHubPathSegmentArbitrary
@@ -54,48 +58,57 @@ const assertRepositoryRefIdProperty = (
5458 fc . assert ( fc . property ( repositoryArbitrary , refIdArbitrary , assertion ) )
5559}
5660
61+ const projectFeatureLabelWithContainer = (
62+ { owner, repo } : GeneratedRepository ,
63+ containerName : string
64+ ) : string =>
65+ projectTerminalLabel ( {
66+ containerName,
67+ displayName : `${ owner } /${ repo } ` ,
68+ repoRef : "feature-x" ,
69+ repoUrl : `https://github.com/${ owner } /${ repo } .git`
70+ } )
71+
5772describe ( "projectTerminalLabel" , ( ) => {
58- it ( "renders GitHub issue source context and container identity" , ( ) => {
73+ it ( "renders GitHub issue source URL and container identity" , ( ) => {
5974 expect ( projectTerminalLabel ( {
6075 containerName : "dg-repo-issue-7" ,
6176 displayName : "org/repo" ,
6277 repoRef : "issue-7" ,
6378 repoUrl : "https://github.com/org/repo.git"
64- } ) ) . toBe ( "org/repo | issue #7 ( https://github.com/org/repo/issues/7) | container dg-repo-issue-7" )
79+ } ) ) . toBe ( "https://github.com/org/repo/issues/7 | container dg-repo-issue-7" )
6580 } )
6681
67- it ( "renders GitHub pull request source context from pull refs" , ( ) => {
82+ it ( "renders GitHub pull request source URL from pull refs" , ( ) => {
6883 expect ( projectTerminalLabel ( {
6984 containerName : "dg-repo-pr-42" ,
7085 displayName : "org/repo" ,
7186 repoRef : "refs/pull/42/head" ,
7287 repoUrl : "git@github.com:org/repo.git"
73- } ) ) . toBe ( "org/repo | PR #42 ( https://github.com/org/repo/pull/42) | container dg-repo-pr-42" )
88+ } ) ) . toBe ( "https://github.com/org/repo/pull/42 | container dg-repo-pr-42" )
7489 } )
7590
7691 it ( "renders repository source context for ordinary refs" , ( ) => {
7792 expect ( projectTerminalLabel ( {
7893 displayName : "org/repo" ,
7994 repoRef : "feature-x" ,
8095 repoUrl : "https://github.com/org/repo.git"
81- } ) ) . toBe ( "org/repo | source https://github.com/org/repo.git (feature-x)" )
96+ } ) ) . toBe ( "https://github.com/org/repo.git (feature-x)" )
8297 } )
8398
84- it ( "preserves issue markers and GitHub issue URLs for generated issue refs " , ( ) => {
99+ it ( "property-based invariant: issue-N mapping generates canonical GitHub issue URLs" , ( ) => {
85100 assertRepositoryRefIdProperty ( ( { owner, repo } , issueId ) => {
86101 const label = projectTerminalLabel ( {
87102 displayName : `${ owner } /${ repo } ` ,
88103 repoRef : `issue-${ issueId } ` ,
89104 repoUrl : `https://github.com/${ owner } /${ repo } .git`
90105 } )
91106
92- expect ( label ) . toBe (
93- `${ owner } /${ repo } | issue #${ issueId } (https://github.com/${ owner } /${ repo } /issues/${ issueId } )`
94- )
107+ expect ( label ) . toBe ( `https://github.com/${ owner } /${ repo } /issues/${ issueId } ` )
95108 } )
96109 } )
97110
98- it ( "preserves PR and MR markers for generated review refs " , ( ) => {
111+ it ( "property-based invariant: PR/ MR markers generate review source context " , ( ) => {
99112 fc . assert (
100113 fc . property (
101114 repositoryArbitrary ,
@@ -111,54 +124,91 @@ describe("projectTerminalLabel", () => {
111124
112125 expect ( label ) . toBe (
113126 refKind === "pull"
114- ? `${ owner } / ${ repo } | PR # ${ reviewId } ( https://github.com/${ owner } /${ repo } /pull/${ reviewId } ) `
115- : `${ owner } / ${ repo } | MR #${ reviewId } `
127+ ? `https://github.com/${ owner } /${ repo } /pull/${ reviewId } `
128+ : `MR #${ reviewId } `
116129 )
117130 }
118131 )
119132 )
120133 } )
121134
122- it ( "uses repoUrl as the base label when displayName is blank" , ( ) => {
135+ it ( "property-based invariant: displayName/repoUrl fallback is deterministic without source context" , ( ) => {
136+ fc . assert (
137+ fc . property (
138+ paddedReadableLabelArbitrary ,
139+ blankLabelArbitrary ,
140+ emptyOrMainRefArbitrary ,
141+ ( displayName , repoUrl , repoRef ) => {
142+ expect ( projectTerminalLabel ( {
143+ displayName,
144+ repoRef,
145+ repoUrl
146+ } ) ) . toBe ( displayName . trim ( ) )
147+ }
148+ )
149+ )
150+
123151 fc . assert (
124- fc . property ( repositoryArbitrary , fc . constantFrom ( "" , " " , " " ) , ( { owner, repo } , displayName ) => {
152+ fc . property (
153+ paddedReadableLabelArbitrary ,
154+ blankLabelArbitrary ,
155+ emptyOrMainRefArbitrary ,
156+ ( repoUrl , displayName , repoRef ) => {
157+ expect ( projectTerminalLabel ( {
158+ displayName,
159+ repoRef,
160+ repoUrl
161+ } ) ) . toBe ( repoUrl . trim ( ) )
162+ }
163+ )
164+ )
165+ } )
166+
167+ it ( "property-based invariant: repoUrl fallback is used when displayName is blank" , ( ) => {
168+ fc . assert (
169+ fc . property ( repositoryArbitrary , blankLabelArbitrary , ( { owner, repo } , displayName ) => {
125170 const repoUrl = `https://github.com/${ owner } /${ repo } .git`
126171
127172 expect ( projectTerminalLabel ( {
128173 displayName,
129174 repoRef : "main" ,
130175 repoUrl
131- } ) ) . toBe ( ` ${ repoUrl } | source ${ repoUrl } ` )
176+ } ) ) . toBe ( repoUrl )
132177 } )
133178 )
134179 } )
135180
136- it ( "normalizes empty and main refs to source context without ref suffix" , ( ) => {
181+ it ( "property-based invariant: empty/ main ref handling omits ref suffix" , ( ) => {
137182 fc . assert (
138- fc . property ( repositoryArbitrary , fc . constantFrom ( "" , " " , " " , "main" ) , ( { owner, repo } , repoRef ) => {
183+ fc . property ( repositoryArbitrary , emptyOrMainRefArbitrary , ( { owner, repo } , repoRef ) => {
139184 const repoUrl = `https://github.com/${ owner } /${ repo } .git`
140185
141186 expect ( projectTerminalLabel ( {
142187 displayName : `${ owner } /${ repo } ` ,
143188 repoRef,
144189 repoUrl
145- } ) ) . toBe ( ` ${ owner } / ${ repo } | source ${ repoUrl } ` )
190+ } ) ) . toBe ( repoUrl )
146191 } )
147192 )
148193 } )
149194
150- it ( "preserves non-empty container names after trimming" , ( ) => {
195+ it ( "property-based invariant: container handling preserves non-empty container names after trimming" , ( ) => {
151196 fc . assert (
152- fc . property ( repositoryArbitrary , paddedReadableLabelArbitrary , ( { owner, repo } , containerName ) => {
153- const label = projectTerminalLabel ( {
154- containerName,
155- displayName : `${ owner } /${ repo } ` ,
156- repoRef : "feature-x" ,
157- repoUrl : `https://github.com/${ owner } /${ repo } .git`
158- } )
197+ fc . property ( repositoryArbitrary , paddedReadableLabelArbitrary , ( repository , containerName ) => {
198+ const label = projectFeatureLabelWithContainer ( repository , containerName )
159199
160200 expect ( label . endsWith ( ` | container ${ containerName . trim ( ) } ` ) ) . toBe ( true )
161201 } )
162202 )
163203 } )
204+
205+ it ( "property-based invariant: container handling omits blank container names" , ( ) => {
206+ fc . assert (
207+ fc . property ( repositoryArbitrary , blankLabelArbitrary , ( repository , containerName ) => {
208+ const label = projectFeatureLabelWithContainer ( repository , containerName )
209+
210+ expect ( label ) . not . toContain ( "container " )
211+ } )
212+ )
213+ } )
164214} )
0 commit comments