@@ -36,6 +36,9 @@ import * as SourceControlProviderRegistry from "../sourceControl/SourceControlPr
3636import * as SourceControlRateLimit from "@t3tools/source-control-core/server/SourceControlRateLimit" ;
3737import * as ForgejoCli from "@t3tools/source-control-forgejo/server/ForgejoCli" ;
3838import * as ForgejoPullRequestProvider from "@t3tools/source-control-forgejo/server/ForgejoPullRequestProvider" ;
39+ import * as BitbucketApi from "@t3tools/source-control-bitbucket/server/BitbucketApi" ;
40+ import * as BitbucketPullRequestApi from "@t3tools/source-control-bitbucket/server/BitbucketPullRequestApi" ;
41+ import * as BitbucketPullRequestProvider from "@t3tools/source-control-bitbucket/server/BitbucketPullRequestProvider" ;
3942import * as GitHubRest from "@t3tools/source-control-github/server/GitHubRest" ;
4043import * as GitHubCredentials from "@t3tools/source-control-github/server/GitHubCredentials" ;
4144import * as GitHubPullRequestApi from "@t3tools/source-control-github/server/GitHubPullRequestApi" ;
@@ -1287,26 +1290,161 @@ it.effect("keeps one host listed when another is not set up", () =>
12871290 } ) ,
12881291) ;
12891292
1290- it . effect ( "fails as unavailable only when no host can be read" , ( ) =>
1293+ it . effect ( "retains an unavailable host's summary when it is the only queried host" , ( ) =>
1294+ Effect . gen ( function * ( ) {
1295+ for ( const reason of [ "missing-tool" , "unauthenticated" ] as const ) {
1296+ const service = yield * makeService ( {
1297+ projects : [
1298+ project ( {
1299+ id : "p1" ,
1300+ title : "t3code" ,
1301+ workspaceRoot : "/a" ,
1302+ repository : "pingdotgg/t3code" ,
1303+ } ) ,
1304+ ] ,
1305+ providers : [
1306+ fakeProvider ( SourceControlProviderKind . make ( "github" ) , {
1307+ getViewer : ( ) =>
1308+ Effect . fail ( unusable ( SourceControlProviderKind . make ( "github" ) , reason ) ) ,
1309+ } ) ,
1310+ ] ,
1311+ } ) ;
1312+
1313+ for ( const filter of [ { } , { host : "github.com" } ] ) {
1314+ const error = yield * service . list ( { state : "open" , ...filter } ) . pipe ( Effect . flip ) ;
1315+ assert . strictEqual ( error . _tag , "PullRequestUnavailableError" ) ;
1316+ assert . strictEqual (
1317+ error . _tag === "PullRequestUnavailableError" ? error . reason : null ,
1318+ reason === "missing-tool" ? "cli-missing" : "cli-unauthenticated" ,
1319+ ) ;
1320+ assert . isDefined ( error . listResult ) ;
1321+ const result = error . listResult ! ;
1322+ assert . deepStrictEqual ( result . entries , [ ] ) ;
1323+ assert . deepStrictEqual ( result . viewers , { } ) ;
1324+ assert . deepStrictEqual (
1325+ result . providers . map ( ( { host, configured, projectIds } ) => ( {
1326+ host,
1327+ configured,
1328+ projectIds : projectIds ?. map ( String ) ,
1329+ } ) ) ,
1330+ [ { host : "github.com" , configured : false , projectIds : [ "p1" ] } ] ,
1331+ ) ;
1332+ assert . strictEqual ( result . providers [ 0 ] ?. detail ?. includes ( "GitHub" ) , true ) ;
1333+ assert . deepStrictEqual (
1334+ result . errors . map ( ( { projectId } ) => projectId ) ,
1335+ [ "p1" ] ,
1336+ ) ;
1337+ }
1338+ }
1339+ } ) ,
1340+ ) ;
1341+
1342+ it . effect (
1343+ "retains a Bitbucket host summary when saved credentials are rejected with HTTP 403" ,
1344+ ( ) =>
1345+ Effect . gen ( function * ( ) {
1346+ const provider = yield * BitbucketPullRequestProvider . make . pipe (
1347+ Effect . provide (
1348+ Layer . mock ( BitbucketPullRequestApi . BitbucketPullRequestApi ) ( {
1349+ getViewer : ( ) =>
1350+ Effect . fail (
1351+ new BitbucketApi . BitbucketResponseError ( {
1352+ operation : "request" ,
1353+ status : 403 ,
1354+ responseBodyLength : 0 ,
1355+ } ) ,
1356+ ) ,
1357+ } ) ,
1358+ ) ,
1359+ ) ;
1360+ const service = yield * makeService ( {
1361+ projects : [
1362+ project ( {
1363+ id : "bitbucket" ,
1364+ title : "Widget" ,
1365+ workspaceRoot : "/widget" ,
1366+ repository : "acme/widget" ,
1367+ provider : "bitbucket" ,
1368+ host : "bitbucket.org" ,
1369+ } ) ,
1370+ ] ,
1371+ providers : [ provider ] ,
1372+ } ) ;
1373+ for ( const filter of [ { } , { host : "bitbucket.org" } ] ) {
1374+ const error = yield * service . list ( { state : "open" , ...filter } ) . pipe ( Effect . flip ) ;
1375+ assert . strictEqual ( error . _tag , "PullRequestOperationError" ) ;
1376+ assert . isDefined ( error . listResult ) ;
1377+ const result = error . listResult ! ;
1378+ assert . deepStrictEqual (
1379+ result . providers . map ( ( { host, configured, projectIds } ) => ( {
1380+ host,
1381+ configured,
1382+ projectIds : projectIds ?. map ( String ) ,
1383+ } ) ) ,
1384+ [ { host : "bitbucket.org" , configured : false , projectIds : [ "bitbucket" ] } ] ,
1385+ ) ;
1386+ assert . deepStrictEqual ( result . entries , [ ] ) ;
1387+ assert . deepStrictEqual (
1388+ result . errors . map ( ( { projectId } ) => projectId ) ,
1389+ [ "bitbucket" ] ,
1390+ ) ;
1391+ }
1392+ } ) ,
1393+ ) ;
1394+
1395+ it . effect ( "identifies every SSH worktree behind an unavailable refined Forgejo authority" , ( ) =>
12911396 Effect . gen ( function * ( ) {
12921397 const service = yield * makeService ( {
1293- projects : [
1294- project ( { id : "p1" , title : "t3code" , workspaceRoot : "/a" , repository : "pingdotgg/t3code" } ) ,
1295- ] ,
1398+ projects : [ "ssh" , "ssh-worktree" ] . map ( ( id ) =>
1399+ project ( {
1400+ id,
1401+ title : id ,
1402+ workspaceRoot : `/${ id } ` ,
1403+ repository : "team/repo" ,
1404+ provider : "forgejo" ,
1405+ host : "ssh.forge.example" ,
1406+ remoteUrl :
"ssh://[email protected] :2222/team/repo.git" , 1407+ } ) ,
1408+ ) ,
12961409 providers : [
1297- fakeProvider ( SourceControlProviderKind . make ( "github" ) , {
1298- getViewer : ( ) =>
1299- Effect . fail ( unusable ( SourceControlProviderKind . make ( "github" ) , "missing-tool" ) ) ,
1410+ fakeProvider ( SourceControlProviderKind . make ( "forgejo" ) , {
1411+ getViewer : ( { host } ) => {
1412+ assert . strictEqual ( host , "forge.example:3000" ) ;
1413+ return Effect . fail (
1414+ unusable ( SourceControlProviderKind . make ( "forgejo" ) , "unauthenticated" ) ,
1415+ ) ;
1416+ } ,
1417+ listChangeRequests : ( ) => Effect . die ( "An unauthenticated host must not be queried" ) ,
13001418 } ) ,
13011419 ] ,
1420+ resolveHandle : ( { context } ) =>
1421+ Effect . succeed ( {
1422+ context : {
1423+ ...context ! ,
1424+ provider : {
1425+ kind : SourceControlProviderKind . make ( "forgejo" ) ,
1426+ name : "Forgejo" ,
1427+ baseUrl : "http://forge.example:3000" ,
1428+ } ,
1429+ } ,
1430+ provider : undefined as never ,
1431+ } ) ,
13021432 } ) ;
1303-
13041433 const error = yield * service . list ( { state : "open" } ) . pipe ( Effect . flip ) ;
1305-
1306- assert . strictEqual ( error . _tag , "PullRequestUnavailableError" ) ;
1307- assert . strictEqual (
1308- error . _tag === "PullRequestUnavailableError" ? error . reason : null ,
1309- "cli-missing" ,
1434+ assert . isDefined ( error . listResult ) ;
1435+ const result = error . listResult ! ;
1436+ assert . deepStrictEqual (
1437+ result . providers . map ( ( { host, configured, projectIds } ) => ( {
1438+ host,
1439+ configured,
1440+ projectIds : projectIds ?. map ( String ) ,
1441+ } ) ) ,
1442+ [ { host : "forge.example:3000" , configured : false , projectIds : [ "ssh" , "ssh-worktree" ] } ] ,
1443+ ) ;
1444+ assert . deepStrictEqual ( result . entries , [ ] ) ;
1445+ assert . deepStrictEqual (
1446+ result . errors . map ( ( { projectId } ) => projectId ) ,
1447+ [ "ssh" ] ,
13101448 ) ;
13111449 } ) ,
13121450) ;
@@ -1342,6 +1480,7 @@ it.effect("reads a repository once when several worktrees share it", () =>
13421480
13431481 assert . strictEqual ( calls , 1 ) ;
13441482 assert . strictEqual ( result . entries . length , 1 ) ;
1483+ assert . deepStrictEqual ( result . providers [ 0 ] ?. projectIds ?. map ( String ) , [ "p1" , "p2" ] ) ;
13451484 } ) ,
13461485) ;
13471486
0 commit comments