@@ -923,6 +923,213 @@ Adding more text to ensure minimum length requirement is satisfied here.`
923923 } )
924924 } )
925925
926+ describe ( "E2E: actual scripts with --dry-run" , ( ) => {
927+ it ( "should run actual postinstall.mjs with --dry-run" , async ( ) => {
928+ // Run the actual postinstall script with --dry-run flag
929+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" ] , {
930+ cwd : process . cwd ( ) , // Use actual project directory
931+ stdout : "pipe" ,
932+ stderr : "pipe" ,
933+ } )
934+
935+ const exitCode = await proc . exited
936+ const stdout = await new Response ( proc . stdout ) . text ( )
937+ const stderr = await new Response ( proc . stderr ) . text ( )
938+
939+ // Should succeed
940+ expect ( exitCode ) . toBe ( 0 )
941+
942+ // Should indicate dry-run mode
943+ expect ( stdout ) . toContain ( "[DRY-RUN]" )
944+ expect ( stdout ) . toContain ( "opencode-plugin-opencoder: Installing agents..." )
945+
946+ // Should show what would be installed
947+ expect ( stdout ) . toContain ( "Would install: opencoder.md" )
948+ expect ( stdout ) . toContain ( "Would install: opencoder-planner.md" )
949+ expect ( stdout ) . toContain ( "Would install: opencoder-builder.md" )
950+
951+ // Should show success summary
952+ expect ( stdout ) . toContain ( "Successfully installed 3 agent(s)" )
953+
954+ // Should not have errors
955+ expect ( stderr ) . toBe ( "" )
956+ } )
957+
958+ it ( "should run actual postinstall.mjs with --dry-run --verbose" , async ( ) => {
959+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" , "--verbose" ] , {
960+ cwd : process . cwd ( ) ,
961+ stdout : "pipe" ,
962+ stderr : "pipe" ,
963+ } )
964+
965+ const exitCode = await proc . exited
966+ const stdout = await new Response ( proc . stdout ) . text ( )
967+
968+ expect ( exitCode ) . toBe ( 0 )
969+
970+ // Should include verbose output
971+ expect ( stdout ) . toContain ( "[VERBOSE]" )
972+ expect ( stdout ) . toContain ( "Package root:" )
973+ expect ( stdout ) . toContain ( "Source directory:" )
974+ expect ( stdout ) . toContain ( "Target directory:" )
975+ expect ( stdout ) . toContain ( "Dry run: true" )
976+ expect ( stdout ) . toContain ( "Markdown files found: 3" )
977+ expect ( stdout ) . toContain ( "Validation passed" )
978+ } )
979+
980+ it ( "should run actual preuninstall.mjs with --dry-run" , async ( ) => {
981+ // First, ensure the target directory exists with installed agents
982+ // We'll create the target directory structure for this test
983+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
984+
985+ // Create target directory if it doesn't exist
986+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
987+
988+ // Copy agent files to target (simulate a real installation)
989+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
990+ for ( const file of agentFiles ) {
991+ const sourcePath = join ( process . cwd ( ) , "agents" , file )
992+ const targetPath = join ( AGENTS_TARGET_DIR , file )
993+ if ( existsSync ( sourcePath ) ) {
994+ const content = readFileSync ( sourcePath , "utf-8" )
995+ writeFileSync ( targetPath , content )
996+ }
997+ }
998+
999+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" ] , {
1000+ cwd : process . cwd ( ) ,
1001+ stdout : "pipe" ,
1002+ stderr : "pipe" ,
1003+ } )
1004+
1005+ const exitCode = await proc . exited
1006+ const stdout = await new Response ( proc . stdout ) . text ( )
1007+ const stderr = await new Response ( proc . stderr ) . text ( )
1008+
1009+ // Should succeed
1010+ expect ( exitCode ) . toBe ( 0 )
1011+
1012+ // Should indicate dry-run mode
1013+ expect ( stdout ) . toContain ( "[DRY-RUN]" )
1014+ expect ( stdout ) . toContain ( "opencode-plugin-opencoder: Removing agents..." )
1015+
1016+ // Should show what would be removed
1017+ expect ( stdout ) . toContain ( "Would remove:" )
1018+
1019+ // Should show removal summary
1020+ expect ( stdout ) . toContain ( "Removed 3 agent(s)" )
1021+
1022+ // Should not have errors
1023+ expect ( stderr ) . toBe ( "" )
1024+
1025+ // Clean up - remove the test agents we installed
1026+ for ( const file of agentFiles ) {
1027+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1028+ if ( existsSync ( targetPath ) ) {
1029+ rmSync ( targetPath )
1030+ }
1031+ }
1032+ } )
1033+
1034+ it ( "should run actual preuninstall.mjs with --dry-run --verbose" , async ( ) => {
1035+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
1036+
1037+ // Create target directory with agents
1038+ mkdirSync ( AGENTS_TARGET_DIR , { recursive : true } )
1039+
1040+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
1041+ for ( const file of agentFiles ) {
1042+ const sourcePath = join ( process . cwd ( ) , "agents" , file )
1043+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1044+ if ( existsSync ( sourcePath ) ) {
1045+ const content = readFileSync ( sourcePath , "utf-8" )
1046+ writeFileSync ( targetPath , content )
1047+ }
1048+ }
1049+
1050+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" , "--verbose" ] , {
1051+ cwd : process . cwd ( ) ,
1052+ stdout : "pipe" ,
1053+ stderr : "pipe" ,
1054+ } )
1055+
1056+ const exitCode = await proc . exited
1057+ const stdout = await new Response ( proc . stdout ) . text ( )
1058+
1059+ expect ( exitCode ) . toBe ( 0 )
1060+
1061+ // Should include verbose output
1062+ expect ( stdout ) . toContain ( "[VERBOSE]" )
1063+ expect ( stdout ) . toContain ( "Package root:" )
1064+ expect ( stdout ) . toContain ( "Source directory:" )
1065+ expect ( stdout ) . toContain ( "Target directory:" )
1066+ expect ( stdout ) . toContain ( "Dry run: true" )
1067+ expect ( stdout ) . toContain ( "Markdown files to remove: 3" )
1068+
1069+ // Clean up
1070+ for ( const file of agentFiles ) {
1071+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1072+ if ( existsSync ( targetPath ) ) {
1073+ rmSync ( targetPath )
1074+ }
1075+ }
1076+ } )
1077+
1078+ it ( "should handle preuninstall --dry-run with no installed agents" , async ( ) => {
1079+ const { AGENTS_TARGET_DIR } = await import ( "../src/paths.mjs" )
1080+
1081+ // Ensure agents are NOT installed (remove them if they exist)
1082+ const agentFiles = [ "opencoder.md" , "opencoder-planner.md" , "opencoder-builder.md" ]
1083+ for ( const file of agentFiles ) {
1084+ const targetPath = join ( AGENTS_TARGET_DIR , file )
1085+ if ( existsSync ( targetPath ) ) {
1086+ rmSync ( targetPath )
1087+ }
1088+ }
1089+
1090+ const proc = Bun . spawn ( [ "node" , "preuninstall.mjs" , "--dry-run" ] , {
1091+ cwd : process . cwd ( ) ,
1092+ stdout : "pipe" ,
1093+ stderr : "pipe" ,
1094+ } )
1095+
1096+ const exitCode = await proc . exited
1097+ const stdout = await new Response ( proc . stdout ) . text ( )
1098+
1099+ expect ( exitCode ) . toBe ( 0 )
1100+
1101+ // Should indicate nothing to remove OR no agents directory
1102+ const hasNoAgentsMsg =
1103+ stdout . includes ( "No agents were installed" ) || stdout . includes ( "No agents directory found" )
1104+ expect ( hasNoAgentsMsg ) . toBe ( true )
1105+ } )
1106+
1107+ it ( "should validate actual agent files pass validation in --dry-run" , async ( ) => {
1108+ // This test verifies that our actual agent files pass the validation
1109+ const proc = Bun . spawn ( [ "node" , "postinstall.mjs" , "--dry-run" , "--verbose" ] , {
1110+ cwd : process . cwd ( ) ,
1111+ stdout : "pipe" ,
1112+ stderr : "pipe" ,
1113+ } )
1114+
1115+ const exitCode = await proc . exited
1116+ const stdout = await new Response ( proc . stdout ) . text ( )
1117+ const stderr = await new Response ( proc . stderr ) . text ( )
1118+
1119+ // Should succeed - all our actual agents should be valid
1120+ expect ( exitCode ) . toBe ( 0 )
1121+
1122+ // Should NOT contain validation failure messages
1123+ expect ( stderr ) . not . toContain ( "Invalid agent file content" )
1124+ expect ( stderr ) . not . toContain ( "File too short" )
1125+ expect ( stderr ) . not . toContain ( "missing YAML frontmatter" )
1126+ expect ( stderr ) . not . toContain ( "missing required fields" )
1127+
1128+ // Should show validation passed for each file
1129+ expect ( stdout ) . toContain ( "Validation passed" )
1130+ } )
1131+ } )
1132+
9261133 describe ( "full install/uninstall cycle" , ( ) => {
9271134 it ( "should install and then cleanly uninstall" , async ( ) => {
9281135 // Create scripts
0 commit comments