@@ -3,12 +3,15 @@ import * as NodeServices from "@effect/platform-node/NodeServices";
33import { assert , it } from "@effect/vitest" ;
44import {
55 AuthSessionId ,
6+ AuthSourceControlWriteScope ,
67 EnvironmentId ,
78 ProjectId ,
89 ThreadId ,
910 ProviderDriverKind ,
1011 ProviderInstanceId ,
12+ SourceControlRepositoryError ,
1113 WS_METHODS ,
14+ WsRpcGroup ,
1215 WsServerRefreshProvidersRpc ,
1316 WsSubscribeServerConfigRpc ,
1417 type OrchestrationV2StoredEvent ,
@@ -58,6 +61,7 @@ import * as ModelManifest from "./provider/ModelManifest.ts";
5861import * as Orchestrator from "./orchestration-v2/Orchestrator.ts" ;
5962import * as OtlpEndpointCheck from "./observability/OtlpEndpointCheck.ts" ;
6063import * as PairingGrantStore from "./auth/PairingGrantStore.ts" ;
64+ import * as RpcAuthorization from "./auth/RpcAuthorization.ts" ;
6165import * as PeerLinkRequests from "./peer/PeerLinkRequests.ts" ;
6266import * as PeerLinks from "./peer/PeerLinks.ts" ;
6367import * as PortScanner from "./preview/PortScanner.ts" ;
@@ -1035,6 +1039,90 @@ it.effect("on-demand clients get provider config without workspace snapshots", (
10351039 ) ,
10361040) ;
10371041
1042+ it . effect ( "a publish whose push fails still refreshes the new remote" , ( ) =>
1043+ Effect . gen ( function * ( ) {
1044+ const refreshes = yield * Queue . unbounded < string > ( ) ;
1045+ const exits = yield * Queue . unbounded < string > ( ) ;
1046+ const services = Layer . mergeAll (
1047+ layerUnusedConfigRpcServices ,
1048+ Layer . mock ( SourceControlRepositoryService . SourceControlRepositoryService ) ( {
1049+ publishRepository : ( input ) =>
1050+ Effect . fail (
1051+ new SourceControlRepositoryError ( {
1052+ provider : input . provider ,
1053+ operation : "publishRepository" ,
1054+ detail : 'Created owner/repo and added it as remote "origin", but the push failed.' ,
1055+ } ) ,
1056+ ) ,
1057+ } ) ,
1058+ Layer . mock ( RepositoryIdentityResolver . RepositoryIdentityResolver ) ( {
1059+ resolve : ( cwd ) => Queue . offer ( refreshes , `identity ${ cwd } ` ) . pipe ( Effect . as ( null ) ) ,
1060+ } ) ,
1061+ Layer . mock ( VcsStatusBroadcaster . VcsStatusBroadcaster ) ( {
1062+ refreshStatus : ( cwd ) =>
1063+ Queue . offer ( refreshes , `status ${ cwd } ` ) . pipe ( Effect . andThen ( Effect . die ( "unused" ) ) ) ,
1064+ } ) ,
1065+ Layer . mock ( ExternalLauncher . ExternalLauncher ) ( { } ) ,
1066+ Layer . mock ( ProviderRegistry . ProviderRegistry ) ( { } ) ,
1067+ Layer . mock ( UsageLimitSources . UsageLimitSources ) ( { } ) ,
1068+ Layer . mock ( RemoteOpenTargets . RemoteOpenTargets ) ( { } ) ,
1069+ Layer . mock ( DirectEndpoints . DirectEndpoints ) ( { } ) ,
1070+ Layer . mock ( ManagedProjectFolders . ManagedProjectFolders ) ( { namedProjectsRoot : "/unused" } ) ,
1071+ Layer . mock ( ScratchThreadPromotion . ScratchThreadPromotion ) ( { } ) ,
1072+ Layer . mock ( ServerEnvironment . ServerEnvironment ) ( { } ) ,
1073+ Layer . mock ( EnvironmentAuth . EnvironmentAuth ) ( { } ) ,
1074+ ) ;
1075+ const server = yield * RpcServer . makeNoSerialization ( WsRpcGroup , {
1076+ ...WS_RPC_SERVER_OPTIONS ,
1077+ onFromServer : ( response ) =>
1078+ response . _tag === "Exit" ? Queue . offer ( exits , response . exit . _tag ) : Effect . void ,
1079+ } ) . pipe (
1080+ Effect . provide (
1081+ layerWsRpc (
1082+ {
1083+ sessionId : AuthSessionId . make ( "publish-test" ) ,
1084+ subject : "publish-test" ,
1085+ method : "browser-session-cookie" ,
1086+ scopes : [ AuthSourceControlWriteScope ] ,
1087+ } ,
1088+ { } ,
1089+ yield * ServerBrowser . ServerBrowser ,
1090+ ( ) => Effect . void ,
1091+ ) . pipe (
1092+ Layer . provide ( services ) ,
1093+ Layer . merge ( RpcAuthorization . layer ( [ AuthSourceControlWriteScope ] ) ) ,
1094+ ) ,
1095+ ) ,
1096+ ) ;
1097+ yield * server . write ( 0 , {
1098+ _tag : "Request" ,
1099+ id : RpcMessage . RequestId ( "publish" ) ,
1100+ tag : WS_METHODS . sourceControlPublishRepository ,
1101+ payload : {
1102+ cwd : "/repo" ,
1103+ provider : "github" ,
1104+ repository : "owner/repo" ,
1105+ visibility : "private" ,
1106+ } ,
1107+ headers : Headers . empty ,
1108+ } ) ;
1109+ assert . equal ( yield * Queue . take ( exits ) , "Failure" ) ;
1110+ assert . deepEqual (
1111+ [ yield * Queue . take ( refreshes ) , yield * Queue . take ( refreshes ) ] ,
1112+ [ "identity /repo" , "status /repo" ] ,
1113+ ) ;
1114+ } ) . pipe (
1115+ Effect . provide (
1116+ Layer . mergeAll (
1117+ Layer . fresh ( layerConfigSources ) ,
1118+ Layer . mock ( PreviewAutomationBroker . PreviewAutomationBroker ) ( { } ) ,
1119+ Layer . mock ( ServerBrowser . ServerBrowser ) ( { } ) ,
1120+ ) ,
1121+ ) ,
1122+ Effect . scoped ,
1123+ ) ,
1124+ ) ;
1125+
10381126// Compaction keeps the oldest and newest event per message, so a cursor older
10391127// than the deleted rows still replays each message where the client would
10401128// first have seen it (reducers append on first sight) and reaches the same
0 commit comments