@@ -750,9 +750,9 @@ func TestDependencyInstallerInstallFromFreshClone(t *testing.T) {
750750 assert .Empty (t , di .logs .stateUpdates , "Should have no state updates" )
751751 })
752752
753- t .Run ("Already installed, up-to-date hash BUT modified local file" , func (t * testing.T ) {
754- // This is the CRITICAL test case: network hash matches flow.json hash,
755- // but local file has been tampered with. This should FAIL.
753+ t .Run ("Already installed, up-to-date hash BUT modified local file - user repairs " , func (t * testing.T ) {
754+ // Network hash matches flow.json hash, but local file has been tampered with
755+ // Should auto-repair WITHOUT prompting (flow.json is source of truth)
756756 _ , state , _ := util .TestMocks (t )
757757
758758 contractCode := []byte (`access(all) contract Hello { access(all) fun sayHello(): String { return "Hello, World!" } }` )
@@ -796,7 +796,7 @@ func TestDependencyInstallerInstallFromFreshClone(t *testing.T) {
796796 gw .GetAccount .Return (acc , nil )
797797 })
798798
799- // Mock prompter - should NOT be called (no update prompt since hashes match)
799+ // No prompter needed - auto-repairs when network agrees with flow.json
800800 mockPrompter := & mockPrompter {responses : []bool {}}
801801
802802 di := & DependencyInstaller {
@@ -819,14 +819,99 @@ func TestDependencyInstallerInstallFromFreshClone(t *testing.T) {
819819 }
820820
821821 err = di .Install ()
822- // Should FAIL because local file has been modified (tampering detected)
823- assert .Error (t , err , "Should fail when local file is modified even if network hash matches" )
824- assert .Contains (t , err .Error (), "local file has been modified" , "Error should mention file modification" )
825- assert .Contains (t , err .Error (), "hash mismatch" , "Error should mention hash mismatch" )
826- assert .Contains (t , err .Error (), "Hello" , "Error should mention the dependency name" )
822+ // Should SUCCEED - auto-repaired without prompting
823+ assert .NoError (t , err , "Should auto-repair when network agrees with flow.json" )
827824
828- // Verify no prompts occurred (integrity check happens before any prompts)
829- assert .Equal (t , 0 , mockPrompter .index , "No prompts should have been shown" )
825+ // Verify file WAS repaired
826+ fileContent , err := state .ReaderWriter ().ReadFile (filePath )
827+ assert .NoError (t , err )
828+ assert .Contains (t , string (fileContent ), "Hello, World!" , "Should have correct version" )
829+ assert .NotContains (t , string (fileContent ), "HACKED" , "Should not have hacked version" )
830+
831+ // Verify NO prompt was shown (auto-repair because network agrees with flow.json)
832+ assert .Equal (t , 0 , mockPrompter .index , "Should not prompt when network agrees with flow.json" )
833+ })
834+
835+ t .Run ("Already installed, up-to-date hash BUT modified local file - skip prompts mode" , func (t * testing.T ) {
836+ // Network hash matches flow.json hash, but local file has been tampered with
837+ // Should auto-repair even with --skip-update-prompts (no network change)
838+ _ , state , _ := util .TestMocks (t )
839+
840+ contractCode := []byte (`access(all) contract Hello { access(all) fun sayHello(): String { return "Hello, World!" } }` )
841+ modifiedContractCode := []byte (`access(all) contract Hello { access(all) fun sayHello(): String { return "Hello, HACKED!" } }` )
842+
843+ // Calculate the hash of the correct contract
844+ hash := sha256 .New ()
845+ hash .Write (contractCode )
846+ contractHash := hex .EncodeToString (hash .Sum (nil ))
847+
848+ // Simulate a dependency with matching hash in flow.json
849+ dep := config.Dependency {
850+ Name : "Hello" ,
851+ Source : config.Source {
852+ NetworkName : "emulator" ,
853+ Address : serviceAddress ,
854+ ContractName : "Hello" ,
855+ },
856+ Hash : contractHash , // Hash matches what's on network
857+ }
858+
859+ state .Dependencies ().AddOrUpdate (dep )
860+
861+ // Create a MODIFIED file (different from what hash says it should be)
862+ filePath := fmt .Sprintf ("imports/%s/Hello.cdc" , serviceAddress .String ())
863+ err := state .ReaderWriter ().MkdirAll (filepath .Dir (filePath ), 0755 )
864+ assert .NoError (t , err )
865+ err = state .ReaderWriter ().WriteFile (filePath , modifiedContractCode , 0644 )
866+ assert .NoError (t , err )
867+
868+ gw := mocks .DefaultMockGateway ()
869+
870+ gw .GetAccount .Run (func (args mock.Arguments ) {
871+ addr := args .Get (1 ).(flow.Address )
872+ assert .Equal (t , addr .String (), serviceAddress .String ())
873+ acc := tests .NewAccountWithAddress (addr .String ())
874+ acc .Contracts = map [string ][]byte {
875+ "Hello" : contractCode , // Network has the correct version
876+ }
877+
878+ gw .GetAccount .Return (acc , nil )
879+ })
880+
881+ // No prompter needed - auto-repairs regardless of flags
882+ mockPrompter := & mockPrompter {responses : []bool {}}
883+
884+ di := & DependencyInstaller {
885+ Gateways : map [string ]gateway.Gateway {
886+ config .EmulatorNetwork .Name : gw .Mock ,
887+ config .TestnetNetwork .Name : gw .Mock ,
888+ config .MainnetNetwork .Name : gw .Mock ,
889+ },
890+ Logger : logger ,
891+ State : state ,
892+ SaveState : true ,
893+ TargetDir : "" ,
894+ SkipDeployments : true ,
895+ SkipAlias : true ,
896+ SkipUpdatePrompts : true , // Should still auto-repair (no network change)
897+ dependencies : make (map [string ]config.Dependency ),
898+ accountAliases : make (map [string ]map [string ]flow.Address ),
899+ pendingPrompts : make ([]pendingPrompt , 0 ),
900+ prompter : mockPrompter ,
901+ }
902+
903+ err = di .Install ()
904+ // Should SUCCEED - auto-repaired even with --skip-update-prompts
905+ assert .NoError (t , err , "Should succeed even with --skip-update-prompts (no network change)" )
906+
907+ // Verify file WAS repaired
908+ fileContent , err := state .ReaderWriter ().ReadFile (filePath )
909+ assert .NoError (t , err )
910+ assert .Contains (t , string (fileContent ), "Hello, World!" , "Should have correct version" )
911+ assert .NotContains (t , string (fileContent ), "HACKED" , "Should not have hacked version" )
912+
913+ // Verify no prompts (auto-repair because network agrees with flow.json)
914+ assert .Equal (t , 0 , mockPrompter .index , "Should not prompt when network agrees with flow.json" )
830915 })
831916
832917 t .Run ("Already installed, outdated hash - user accepts" , func (t * testing.T ) {
0 commit comments