-
Notifications
You must be signed in to change notification settings - Fork 142
Backward compatibility testing infrastructure #2031
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
darioAnongba
wants to merge
6
commits into
main
Choose a base branch
from
backward-compat-testing
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
ddeb63e
itest: add NewNodeWithBinary and UpgradeNode to harness
darioAnongba d6b85db
scripts: add build-compat-binary for historical releases
darioAnongba 41bdff8
rfqmsg: add versioned backward compatibility fixture tests
darioAnongba 3e20f12
tapchannelmsg: add versioned backward compatibility fixture tests
darioAnongba e86aa1d
itest: add backward compatibility integration test wrapper
darioAnongba d142111
make+ci: add backward compatibility Makefile targets and CI workflow
darioAnongba File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| name: Backward Compatibility Tests | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| version: | ||
| description: 'Historical version to test against (e.g. v0.8.0). Leave empty to test all configured versions.' | ||
| required: false | ||
| type: string | ||
| push: | ||
| branches: | ||
| - 'release/**' | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| defaults: | ||
| run: | ||
| shell: bash | ||
|
|
||
| env: | ||
| GOPATH: /home/runner/go | ||
|
|
||
| jobs: | ||
| compat-tests: | ||
| name: Compat Tests | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 60 | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 # Full history needed for git worktree builds. | ||
|
|
||
| - name: Set up Go | ||
| uses: actions/setup-go@v5 | ||
| with: | ||
| go-version-file: go.mod | ||
|
|
||
| - name: Cache compat binaries | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: ~/.tapd-compat-bins | ||
| key: compat-bins-${{ runner.os }}-${{ runner.arch }} | ||
|
|
||
| - name: Build current integrated binary | ||
| run: make build-itest | ||
|
|
||
| - name: Run backward compatibility tests | ||
| run: make itest-cc-compat |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| //go:build itest | ||
|
|
||
| package custom_channels | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "os" | ||
| "os/exec" | ||
| "path/filepath" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/lightninglabs/taproot-assets/itest" | ||
| "github.com/lightningnetwork/lnd/lntest" | ||
| "github.com/lightningnetwork/lnd/lntest/miner" | ||
| "github.com/lightningnetwork/lnd/lntest/node" | ||
| "github.com/lightningnetwork/lnd/lnwallet/chainfee" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // compatVersions lists the historical release versions to test backward | ||
| // compatibility against. Update this list before each release to include | ||
| // the latest 2 minor versions. | ||
| var compatVersions = []string{ | ||
| // TODO: Uncomment once v0.8.0 is released and the integrated binary | ||
| // can be built from that tag. | ||
| // "v0.8.0", | ||
| } | ||
|
|
||
| // compatTestCases is the subset of test cases that exercise critical | ||
| // backward compatibility surfaces: channel open/close, routing, force | ||
| // close, and upgrade. | ||
| var compatTestCases = []*ccTestCase{ | ||
| { | ||
| name: "core", | ||
| test: testCustomChannels, | ||
| }, | ||
| { | ||
| name: "force close", | ||
| test: testCustomChannelsForceClose, | ||
| }, | ||
| { | ||
| name: "v1 upgrade", | ||
| test: testCustomChannelsV1Upgrade, | ||
| }, | ||
| } | ||
|
|
||
| // buildCompatBinary builds or retrieves a cached tapd-integrated binary for | ||
| // the given version tag. It returns the path to the binary. | ||
| func buildCompatBinary(t *testing.T, version string) string { | ||
| t.Helper() | ||
|
|
||
| // Locate the build script relative to the repo root. | ||
| repoRoot, err := exec.Command( | ||
| "git", "rev-parse", "--show-toplevel", | ||
| ).Output() | ||
| require.NoError(t, err, "unable to find repo root") | ||
|
|
||
| script := filepath.Join( | ||
| string(repoRoot[:len(repoRoot)-1]), | ||
| "scripts", "build-compat-binary.sh", | ||
| ) | ||
|
|
||
| // Run the build script. It prints the binary path on stdout. | ||
| //nolint:gosec | ||
| cmd := exec.Command(script, version) | ||
| cmd.Stderr = os.Stderr | ||
| out, err := cmd.Output() | ||
| require.NoError(t, err, "unable to build compat binary for %s", | ||
| version) | ||
|
|
||
| binaryPath := string(out[:len(out)-1]) // trim trailing newline | ||
| require.FileExists(t, binaryPath) | ||
|
|
||
| return binaryPath | ||
| } | ||
|
|
||
| // TestCustomChannelsCompat runs a subset of custom channel tests with one | ||
| // node running an older binary version for each historical version in | ||
| // compatVersions. This test is gated behind the `compat` build tag and | ||
| // intended to run only on release branches or via manual dispatch. | ||
| // | ||
| // The test creates a fresh network harness for each version, builds or | ||
| // retrieves the old binary, and runs each compat test case. In each test, | ||
| // one node (typically the "old" peer) is started with the historical binary | ||
| // via NewNodeWithBinary, while the other nodes use the current build. | ||
| func TestCustomChannelsCompat(t *testing.T) { | ||
| if len(compatVersions) == 0 { | ||
| t.Skip("no compat versions configured") | ||
| } | ||
|
|
||
| for _, version := range compatVersions { | ||
| version := version | ||
| t.Run(version, func(t *testing.T) { | ||
| runCompatSuite(t, version) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // runCompatSuite runs all compat test cases for a single historical version. | ||
| func runCompatSuite(t *testing.T, version string) { | ||
| t.Helper() | ||
|
|
||
| // Build or retrieve the old binary. | ||
| oldBinary := buildCompatBinary(t, version) | ||
| t.Logf("Using compat binary for %s: %s", version, oldBinary) | ||
|
|
||
| lntest.MaxBlocksMinedPerTest = 250 | ||
|
|
||
| logDir := node.GetLogDir() | ||
| netName := miner.HarnessNetParams.Name | ||
| for _, dir := range []string{".minerlogs", ".backendlogs"} { | ||
| path := fmt.Sprintf("%s/%s/%s", logDir, dir, netName) | ||
| require.NoError(t, os.MkdirAll(path, 0750)) | ||
| } | ||
|
|
||
| ctx, cancel := context.WithCancel(context.Background()) | ||
| defer cancel() | ||
|
|
||
| m := miner.NewMiner(ctx, t) | ||
| require.NoError(t, m.SetUp(true, 50)) | ||
| require.NoError(t, m.Client.NotifyNewTransactions(false)) | ||
| t.Cleanup(func() { m.Stop() }) | ||
|
|
||
| numBlocks := miner.HarnessNetParams.MinerConfirmationWindow * 2 | ||
| m.GenerateBlocks(numBlocks) | ||
|
|
||
| chainBackend, cleanup, err := lntest.NewBackend( | ||
| m.P2PAddress(), miner.HarnessNetParams, | ||
| ) | ||
| require.NoError(t, err) | ||
| defer func() { | ||
| require.NoError(t, cleanup()) | ||
| }() | ||
| require.NoError(t, chainBackend.ConnectMiner()) | ||
|
|
||
| feeService := lntest.NewFeeService(t) | ||
| feeService.SetFeeRate(chainfee.FeePerKwFloor, 1) | ||
| require.NoError(t, feeService.Start()) | ||
| t.Cleanup(func() { | ||
| require.NoError(t, feeService.Stop()) | ||
| }) | ||
|
|
||
| net := itest.NewIntegratedNetworkHarness( | ||
| t, "../tapd-integrated-itest", chainBackend, | ||
| miner.HarnessNetParams, | ||
| ) | ||
| net.Miner = m | ||
| net.FeeServiceURL = feeService.URL() | ||
| net.FeeService = feeService | ||
| defer net.TearDown() | ||
|
|
||
| // Store the old binary path in the harness so test cases can | ||
| // retrieve it. We use an environment variable as a simple | ||
| // side channel. | ||
| t.Setenv("COMPAT_OLD_BINARY", oldBinary) | ||
|
|
||
| for _, tc := range compatTestCases { | ||
| tc := tc | ||
| success := t.Run(tc.name, func(t1 *testing.T) { | ||
| ht := &ccHarnessTest{ | ||
| t: t1, | ||
| testCase: tc, | ||
| lndHarness: net, | ||
| } | ||
| ctxt, cancel := context.WithTimeout( | ||
| ctx, 10*time.Minute, | ||
| ) | ||
| defer cancel() | ||
|
|
||
| tc.test(ctxt, net, ht) | ||
| }) | ||
|
|
||
| net.TearDown() | ||
|
|
||
| if !success { | ||
| t.Logf("Failure time: %v", time.Now().Format( | ||
| "2006-01-02 15:04:05.000", | ||
| )) | ||
| return | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using environment variables to pass data between test setup and test cases can be brittle. A more robust approach would be to extend the
ccHarnessTeststruct to include theoldBinarypath. This would make the data flow explicit and avoid potential issues with parallel test execution if not handled carefully, even thought.Setenvprovides some protection.For example:
This would require modifying the
ccHarnessTeststruct and how it's used in the test cases, but it leads to cleaner and more maintainable test code.