Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions internal/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,24 +194,36 @@ func testCode(

// Resolve network labels using flow.json state
resolveNetworkFromState := func(label string) (string, bool) {
network, err := state.Networks().ByName(strings.ToLower(strings.TrimSpace(label)))
normalizedLabel := strings.ToLower(strings.TrimSpace(label))
network, err := state.Networks().ByName(normalizedLabel)
if err != nil || network == nil {
return "", false
}
if strings.TrimSpace(network.Host) == "" {

// If network has a fork, resolve the fork network's host
host := strings.TrimSpace(network.Host)
if network.Fork != "" {
forkName := strings.ToLower(strings.TrimSpace(network.Fork))
forkNetwork, err := state.Networks().ByName(forkName)
if err != nil {
return "", false
}
host = strings.TrimSpace(forkNetwork.Host)
}

if host == "" {
return "", false
}

// Track network resolution for current test file (indicates pragma-based fork usage)
// Only track if it's not the default "testing" network
normalizedLabel := strings.ToLower(strings.TrimSpace(label))
if currentTestFile != "" && normalizedLabel != "testing" {
if _, exists := fileNetworkResolutions[currentTestFile]; !exists {
fileNetworkResolutions[currentTestFile] = normalizedLabel
}
}

return network.Host, true
return host, true
}

// Configure fork mode if requested
Expand Down
349 changes: 349 additions & 0 deletions internal/test/test_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -900,3 +900,352 @@ func TestForkMode_AutodetectFailureRequiresExplicitNetwork(t *testing.T) {
require.Error(t, err)
assert.ErrorContains(t, err, "failed to get chain ID from fork host")
}

func TestNetworkForkResolution_Success(t *testing.T) {
t.Parallel()

_, state, _ := util.TestMocks(t)

// Add mainnet network with a host
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet",
Host: "access.mainnet.nodes.onflow.org:9000",
})

// Add mainnet-fork that references mainnet via Fork field (no host)
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet-fork",
Fork: "mainnet",
})

// Create a simple test that uses the test_fork pragma
testScript := []byte(`
#test_fork(network: "mainnet-fork", height: nil)

import Test

access(all) fun testSimple() {
Test.assert(true)
}
`)

testFiles := map[string][]byte{
"test_fork_resolution.cdc": testScript,
}

result, err := testCode(testFiles, state, flagsTests{})

require.NoError(t, err)
require.Len(t, result.Results, 1)
assert.NoError(t, result.Results["test_fork_resolution.cdc"][0].Error)
}

func TestNetworkForkResolution_ForkNetworkNotFound(t *testing.T) {
t.Parallel()

_, state, _ := util.TestMocks(t)

// Add mainnet-fork that references non-existent network
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet-fork",
Fork: "nonexistent",
})

// Create a simple test that uses the fork network
testScript := []byte(`
#test_fork(network: "mainnet-fork", height: nil)

import Test

access(all) fun testSimple() {
Test.assert(true)
}
`)

testFiles := map[string][]byte{
"test_fork_missing.cdc": testScript,
}

_, err := testCode(testFiles, state, flagsTests{})

require.Error(t, err)
assert.ErrorContains(t, err, "could not resolve network")
}

func TestNetworkForkResolution_ForkNetworkHasNoHost(t *testing.T) {
t.Parallel()

_, state, _ := util.TestMocks(t)

// Add mainnet network with no host
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet",
})

// Add mainnet-fork that references mainnet with no host
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet-fork",
Fork: "mainnet",
})

// Create a simple test that uses the fork network
testScript := []byte(`
#test_fork(network: "mainnet-fork", height: nil)

import Test

access(all) fun testSimple() {
Test.assert(true)
}
`)

testFiles := map[string][]byte{
"test_fork_no_host.cdc": testScript,
}

_, err := testCode(testFiles, state, flagsTests{})

// Should fail with network resolution error
require.Error(t, err)
assert.ErrorContains(t, err, "network resolver could not resolve network")
}

func TestNetworkForkResolution_WithOwnHost(t *testing.T) {
t.Parallel()

_, state, _ := util.TestMocks(t)

// Add mainnet network
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet",
Host: "access.mainnet.nodes.onflow.org:9000",
})

// Add mainnet-fork with its own host AND fork field
// Should use mainnet's host (from Fork) not its own
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet-fork",
Host: "127.0.0.1:3569",
Fork: "mainnet",
})

// Create a simple test that uses the fork network
testScript := []byte(`
#test_fork(network: "mainnet-fork", height: nil)

import Test

access(all) fun testSimple() {
Test.assert(true)
}
`)

testFiles := map[string][]byte{
"test_fork_with_host.cdc": testScript,
}

result, err := testCode(testFiles, state, flagsTests{})

require.NoError(t, err)
require.Len(t, result.Results, 1)
assert.NoError(t, result.Results["test_fork_with_host.cdc"][0].Error)
}

func TestContractAddressForkResolution_UsesMainnetForkFirst(t *testing.T) {
t.Parallel()

_, state, _ := util.TestMocks(t)

// Add mainnet network
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet",
Host: "access.mainnet.nodes.onflow.org:9000",
})

// Add mainnet-fork network
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet-fork",
Host: "127.0.0.1:3569",
Fork: "mainnet",
})

// Contract with mainnet-fork specific alias (using proper mainnet-style address)
mainnetForkAddr := flowsdk.HexToAddress("0x1654653399040a61")
contractSource := []byte(`
access(all) contract TestContract {
access(all) var value: Int
init() { self.value = 42 }
}
`)
_ = state.ReaderWriter().WriteFile("TestContract.cdc", contractSource, 0644)

c := config.Contract{
Name: "TestContract",
Location: "TestContract.cdc",
Aliases: config.Aliases{
{
Network: "mainnet-fork",
Address: mainnetForkAddr,
},
},
}
state.Contracts().AddOrUpdate(c)

testScript := []byte(`
#test_fork(network: "mainnet-fork", height: nil)

import Test
import "TestContract"

access(all) fun testUsesMainnetForkAddress() {
// Verify TestContract resolves to the mainnet-fork address
let addr = Type<TestContract>().address!
Test.assertEqual(0x1654653399040a61 as Address, addr)
}
`)

testFiles := map[string][]byte{
"test_mainnet_fork_addr.cdc": testScript,
}

result, err := testCode(testFiles, state, flagsTests{})

require.NoError(t, err)
require.Len(t, result.Results, 1)
assert.NoError(t, result.Results["test_mainnet_fork_addr.cdc"][0].Error)
}

func TestContractAddressForkResolution_FallbackToMainnet(t *testing.T) {
t.Parallel()

_, state, _ := util.TestMocks(t)

// Add mainnet network
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet",
Host: "access.mainnet.nodes.onflow.org:9000",
})

// Add mainnet-fork network
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet-fork",
Host: "127.0.0.1:3569",
Fork: "mainnet",
})

// Contract with ONLY mainnet alias (no mainnet-fork alias)
mainnetAddr := flowsdk.HexToAddress("0xf233dcee88fe0abe")
contractSource := []byte(`
access(all) contract TestContract {
access(all) var value: Int
init() { self.value = 99 }
}
`)
_ = state.ReaderWriter().WriteFile("TestContract.cdc", contractSource, 0644)

c := config.Contract{
Name: "TestContract",
Location: "TestContract.cdc",
Aliases: config.Aliases{
{
Network: "mainnet",
Address: mainnetAddr,
},
},
}
state.Contracts().AddOrUpdate(c)

testScript := []byte(`
#test_fork(network: "mainnet-fork", height: nil)

import Test
import "TestContract"

access(all) fun testFallbackToMainnetAddress() {
// Verify TestContract falls back to mainnet address since mainnet-fork has no alias
let addr = Type<TestContract>().address!
Test.assertEqual(0xf233dcee88fe0abe as Address, addr)
}
`)

testFiles := map[string][]byte{
"test_fallback_mainnet.cdc": testScript,
}

result, err := testCode(testFiles, state, flagsTests{})

require.NoError(t, err)
require.Len(t, result.Results, 1)
assert.NoError(t, result.Results["test_fallback_mainnet.cdc"][0].Error)
}

func TestContractAddressForkResolution_PrioritizesForkOverParent(t *testing.T) {
t.Parallel()

_, state, _ := util.TestMocks(t)

// Add mainnet network
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet",
Host: "access.mainnet.nodes.onflow.org:9000",
})

// Add mainnet-fork network
state.Networks().AddOrUpdate(config.Network{
Name: "mainnet-fork",
Host: "127.0.0.1:3569",
Fork: "mainnet",
})

// Contract with BOTH mainnet and mainnet-fork aliases - should use mainnet-fork first
mainnetAddr := flowsdk.HexToAddress("0x1654653399040a61")
mainnetForkAddr := flowsdk.HexToAddress("0xf233dcee88fe0abe")

contractSource := []byte(`
access(all) contract TestContract {
access(all) var value: Int
init() { self.value = 123 }
}
`)
_ = state.ReaderWriter().WriteFile("TestContract.cdc", contractSource, 0644)

c := config.Contract{
Name: "TestContract",
Location: "TestContract.cdc",
Aliases: config.Aliases{
{
Network: "mainnet",
Address: mainnetAddr,
},
{
Network: "mainnet-fork",
Address: mainnetForkAddr,
},
},
}
state.Contracts().AddOrUpdate(c)

// Should use the mainnet-fork address (0xf233dcee88fe0abe), not mainnet (0x1654653399040a61)
testScript := []byte(`
#test_fork(network: "mainnet-fork", height: nil)

import Test
import "TestContract"

access(all) fun testPrioritizesFork() {
// Verify TestContract uses mainnet-fork address (0xf233dcee88fe0abe), NOT mainnet (0x1654653399040a61)
let addr = Type<TestContract>().address!
Test.assertEqual(0xf233dcee88fe0abe as Address, addr)
}
`)

testFiles := map[string][]byte{
"test_priority.cdc": testScript,
}

result, err := testCode(testFiles, state, flagsTests{})

require.NoError(t, err)
require.Len(t, result.Results, 1)
assert.NoError(t, result.Results["test_priority.cdc"][0].Error)
}
Loading