1+ import * as NodeCrypto from "@effect/platform-node/NodeCrypto" ;
12import * as NodeServices from "@effect/platform-node/NodeServices" ;
3+ import * as HostProcess from "@t3tools/shared/HostProcess" ;
24import { assert , it } from "@effect/vitest" ;
35import * as Console from "effect/Console" ;
46import * as Effect from "effect/Effect" ;
7+ import * as FileSystem from "effect/FileSystem" ;
58import * as Layer from "effect/Layer" ;
9+ import * as Path from "effect/Path" ;
610import * as TestClock from "effect/testing/TestClock" ;
711
812import * as BootService from "../cloud/bootService.ts" ;
913import { runDoctor } from "./doctor.ts" ;
1014import { DatabaseMaintenance } from "../persistence/DatabaseMaintenance.ts" ;
15+ import * as ProcessRunner from "../processRunner.ts" ;
16+ import { acquireServerOwnership } from "../serverOwnership.ts" ;
17+ import { makePersistedServerRuntimeState } from "../serverRuntimeState.ts" ;
18+
19+ // An empty home, so no boot service unit installed on this machine is read.
20+ const layerHost = Layer . mergeAll (
21+ ProcessRunner . layer ,
22+ Layer . effect (
23+ HostProcess . HomeDirectory ,
24+ Effect . gen ( function * ( ) {
25+ return yield * ( yield * FileSystem . FileSystem ) . makeTempDirectoryScoped ( ) ;
26+ } ) ,
27+ ) ,
28+ Layer . succeed ( HostProcess . Environment , { } ) ,
29+ ) . pipe ( Layer . provideMerge ( Layer . mergeAll ( NodeServices . layer , NodeCrypto . layer ) ) ) ;
1130
1231it . effect (
13- "--fix reclaims space with the service paused and keeps the V1 database, and --dry-run changes nothing" ,
32+ "--fix locks the home while the service is paused, names a server that holds it, keeps the V1 database, and --dry-run changes nothing" ,
1433 ( ) =>
1534 Effect . gen ( function * ( ) {
35+ const fs = yield * FileSystem . FileSystem ;
36+ const path = yield * Path . Path ;
37+ const stateDir = yield * fs . makeTempDirectoryScoped ( { prefix : "t3-doctor-" } ) ;
38+ const serverRuntimeStatePath = path . join ( stateDir , "server-runtime.json" ) ;
1639 const calls : string [ ] = [ ] ;
40+ const services =
41+ yield * Effect . context < Effect . Services < ReturnType < typeof acquireServerOwnership > > > ( ) ;
42+ // Stands in for a server launched for the same home, for example over SSH.
43+ const startServer = Effect . scoped ( acquireServerOwnership ( serverRuntimeStatePath ) ) . pipe (
44+ Effect . result ,
45+ Effect . map ( ( result ) =>
46+ calls . push ( result . _tag === "Success" ? "server started" : result . failure . _tag ) ,
47+ ) ,
48+ Effect . provide ( services ) ,
49+ ) ;
1750 const doctor = ( dryRun : boolean ) =>
18- runDoctor ( "/test/statev2.sqlite" , { fix : true , dryRun, interactive : false } ) . pipe (
51+ runDoctor (
52+ { dbPath : path . join ( stateDir , "statev2.sqlite" ) , serverRuntimeStatePath } ,
53+ { fix : true , dryRun, interactive : false } ,
54+ ) . pipe (
1955 Effect . provide (
2056 Layer . mergeAll (
21- NodeServices . layer ,
2257 Layer . mock ( BootService . BootService ) ( {
2358 withStopped : ( operation ) =>
2459 Effect . sync ( ( ) => calls . push ( "pause" ) ) . pipe (
@@ -35,15 +70,17 @@ it.effect(
3570 legacyDatabaseBytes : 4000 ,
3671 } ) ,
3772 compact : ( ) =>
38- Effect . sync ( ( ) => {
39- calls . push ( "compact" ) ;
40- return {
41- beforeBytes : 2000 ,
42- afterBytes : 1000 ,
43- deletedEventCount : 1 ,
44- deletedReceiptCount : 0 ,
45- } ;
46- } ) ,
73+ startServer . pipe (
74+ Effect . map ( ( ) => {
75+ calls . push ( "compact" ) ;
76+ return {
77+ beforeBytes : 2000 ,
78+ afterBytes : 1000 ,
79+ deletedEventCount : 1 ,
80+ deletedReceiptCount : 0 ,
81+ } ;
82+ } ) ,
83+ ) ,
4784 removeLegacyDatabase : ( ) => Effect . sync ( ( ) => calls . push ( "remove legacy" ) ) ,
4885 } ) ,
4986 ) ,
@@ -52,8 +89,32 @@ it.effect(
5289 yield * doctor ( true ) ;
5390 assert . deepEqual ( calls , [ ] ) ;
5491 yield * doctor ( false ) ;
55- assert . deepEqual ( calls , [ "pause" , "compact" , "resume" ] ) ;
56- } ) ,
92+ yield * startServer ;
93+ assert . deepEqual ( calls , [
94+ "pause" ,
95+ "ServerAlreadyRunningError" ,
96+ "compact" ,
97+ "resume" ,
98+ "server started" ,
99+ ] ) ;
100+
101+ calls . length = 0 ;
102+ const state = yield * makePersistedServerRuntimeState ( {
103+ config : { host : undefined , devUrl : undefined } ,
104+ port : 3773 ,
105+ } ) ;
106+ const refused = yield * Effect . scoped (
107+ acquireServerOwnership ( serverRuntimeStatePath ) . pipe (
108+ Effect . tap ( ( owner ) => owner . publish ( state ) ) ,
109+ Effect . andThen ( doctor ( false ) ) ,
110+ ) ,
111+ ) . pipe ( Effect . flip ) ;
112+ assert . equal (
113+ refused . message ,
114+ `Stop T3 Code server process ${ state . pid } , which the background service did not start, before repairing the database.` ,
115+ ) ;
116+ assert . deepEqual ( calls , [ "pause" , "resume" ] ) ;
117+ } ) . pipe ( Effect . provide ( layerHost ) ) ,
57118) ;
58119
59120it . effect ( "warns how long reclaiming can take and reports each step with its elapsed time" , ( ) =>
@@ -65,10 +126,17 @@ it.effect("warns how long reclaiming can take and reports each step with its ela
65126 [ "rebuild-search" , "1 second" ] ,
66127 [ "checkpoint" , "1 second" ] ,
67128 ] as const ;
68- yield * runDoctor ( "/test/statev2.sqlite" , { fix : true , dryRun : false , interactive : false } ) . pipe (
129+ const stateDir = yield * ( yield * FileSystem . FileSystem ) . makeTempDirectoryScoped ( ) ;
130+ const path = yield * Path . Path ;
131+ yield * runDoctor (
132+ {
133+ dbPath : path . join ( stateDir , "statev2.sqlite" ) ,
134+ serverRuntimeStatePath : path . join ( stateDir , "server-runtime.json" ) ,
135+ } ,
136+ { fix : true , dryRun : false , interactive : false } ,
137+ ) . pipe (
69138 Effect . provide (
70139 Layer . mergeAll (
71- NodeServices . layer ,
72140 Layer . mock ( BootService . BootService ) ( {
73141 withStopped : ( operation ) =>
74142 Effect . sync ( ( ) => output . push ( "<pause>" ) ) . pipe (
@@ -125,5 +193,5 @@ it.effect("warns how long reclaiming can take and reports each step with its ela
125193 [ "0:00" , "1:05" , "12:46" , "12:47" ] ,
126194 "each compaction step must print one line with the elapsed time as it starts" ,
127195 ) ;
128- } ) ,
196+ } ) . pipe ( Effect . provide ( layerHost ) ) ,
129197) ;
0 commit comments