-
Notifications
You must be signed in to change notification settings - Fork 76
test(i): Add cross-version P2P test multiplier #5174
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
Merged
islamaliev
merged 37 commits into
sourcenetwork:develop
from
islamaliev:feat/cross-version-multiplier
Sep 1, 2026
Merged
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
397d331
test(i): Generalize per-node test config into a struct
islamaliev 9dec183
test(i): Generalize per-node test config into a struct
islamaliev 54be8d3
Merge remote-tracking branch 'upstream/develop' into refactor/move-no…
islamaliev c5a9064
Move node setup to action package
islamaliev 5690337
Polish
islamaliev 48781b9
Cross-version multiplier
islamaliev bd7faa7
Remove heuristic
islamaliev 136709b
Config
islamaliev 08a7a89
Mark expected docs on external node
islamaliev 4888e3d
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev 6b0fb3f
Corrections
islamaliev 5eb8839
Adjust tests
islamaliev eace9bb
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev b051371
Fixes
islamaliev a166e06
Explicitly exclude cross version from restart tests
islamaliev f958c8a
commit
islamaliev a33f15b
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev da0b261
polish
islamaliev cd4f8c5
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev f498dc9
polish
islamaliev 3f5ade9
Fix
islamaliev 3c17e24
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev 50c37c0
lint
islamaliev 227dfe7
Pass audience to token aud check
islamaliev a6935fd
exclude restarting tests
islamaliev f8734db
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev 6d7e8a1
add identity
islamaliev 31cfda4
skip test
islamaliev b47430d
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev a3f9c00
More excludes
islamaliev 46400e8
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev 16e21bf
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev 8e075d9
comment corrections
islamaliev d82513a
Exclude external if not http client
islamaliev 604a1b9
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev d2d531f
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev e6ffb0d
Merge remote-tracking branch 'upstream/develop' into feat/cross-versi…
islamaliev 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
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,33 @@ | ||
| // Copyright 2026 Democratized Data Foundation | ||
| // | ||
| // This file is part of the DefraDB test suite. | ||
| // | ||
| // The DefraDB test suite is licensed under either: | ||
| // | ||
| // (1) GNU Affero General Public License v3 | ||
| // (2) Business Source License 1.1 | ||
| // | ||
| // See tests/LICENSE for details. | ||
|
|
||
| package action | ||
|
|
||
| import ( | ||
| "os" | ||
|
|
||
| "github.com/sourcenetwork/defradb/tests/state" | ||
| ) | ||
|
|
||
| const documentACPTypeEnvName = "DEFRA_DOCUMENT_ACP_TYPE" | ||
|
|
||
| // DocumentACPType is the document ACP implementation under test. | ||
| // | ||
| // Node setup and the test harness both read this, so it is resolved once here | ||
| // rather than copied into each package. | ||
| var DocumentACPType state.DocumentACPType | ||
|
|
||
| func init() { | ||
| DocumentACPType = state.DocumentACPType(os.Getenv(documentACPTypeEnvName)) | ||
| if DocumentACPType == "" { | ||
| DocumentACPType = state.LocalDocumentACPType | ||
| } | ||
| } |
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,163 @@ | ||
| // Copyright 2026 Democratized Data Foundation | ||
| // | ||
| // This file is part of the DefraDB test suite. | ||
| // | ||
| // The DefraDB test suite is licensed under either: | ||
| // | ||
| // (1) GNU Affero General Public License v3 | ||
| // (2) Business Source License 1.1 | ||
| // | ||
| // See tests/LICENSE for details. | ||
|
|
||
| package action | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "testing" | ||
| "time" | ||
| ) | ||
|
|
||
| const ( | ||
| // eventuallyTimeout is how long [Eventually] retries before giving up. | ||
| eventuallyTimeout = 20 * time.Second | ||
| // eventuallyInterval is how long [Eventually] waits between attempts. | ||
| eventuallyInterval = 100 * time.Millisecond | ||
| ) | ||
|
|
||
| // Eventually runs another action until it stops failing. | ||
| // | ||
| // Use it when the harness cannot tell that something has finished and the test | ||
| // has to keep asking. The clearest case is a node running an older release: it | ||
| // can hold a document written against a schema it has never seen, but it cannot | ||
| // report the commit, so there is no signal to wait for. | ||
| // | ||
| // The wrapped action's assertions are captured rather than failing the test, so | ||
| // a failed attempt is just a retry. The final attempt's failure is reported as | ||
| // this action's failure. | ||
| type Eventually struct { | ||
|
islamaliev marked this conversation as resolved.
|
||
| stateful | ||
|
|
||
| // Action is retried until it passes or the timeout is reached. | ||
| Action Action | ||
|
|
||
| // Timeout overrides the default retry window. | ||
| Timeout time.Duration | ||
| } | ||
|
|
||
| var _ Action = (*Eventually)(nil) | ||
| var _ Stateful = (*Eventually)(nil) | ||
|
|
||
| func (a *Eventually) Execute() { | ||
| timeout := a.Timeout | ||
| if timeout == 0 { | ||
| timeout = eventuallyTimeout | ||
| } | ||
|
|
||
| realT := a.s.T | ||
| // Restore with a defer: a real panic from the nested action skips past the | ||
| // assignment below, which would leave the state pointing at a recorder that | ||
| // nothing reads, silently swallowing later failures. | ||
| defer func() { a.s.T = realT }() | ||
| deadline := time.Now().Add(timeout) | ||
|
|
||
| var lastErr string | ||
| for { | ||
| recorder := &recordingT{TB: realT} | ||
| a.s.T = recorder | ||
| if stateful, ok := a.Action.(Stateful); ok { | ||
| stateful.SetState(a.s) | ||
| } | ||
| failed := a.attempt(recorder) | ||
| a.s.T = realT | ||
|
|
||
| if !failed { | ||
| return | ||
| } | ||
| lastErr = recorder.message | ||
|
|
||
| if time.Now().After(deadline) { | ||
| a.s.T.Errorf("action did not pass within %s: %s", timeout, lastErr) | ||
| a.s.T.FailNow() | ||
| return | ||
| } | ||
| time.Sleep(eventuallyInterval) | ||
| } | ||
| } | ||
|
|
||
| // attempt runs the action once, reporting whether it failed rather than failing | ||
| // the test. | ||
| func (a *Eventually) attempt(recorder *recordingT) (failed bool) { | ||
| defer func() { | ||
| r := recover() | ||
| if r == nil { | ||
| return | ||
| } | ||
| if _, ok := r.(attemptFailure); ok { | ||
| failed = true | ||
| return | ||
| } | ||
| // Anything else is a real panic and belongs to the caller. | ||
| panic(r) | ||
| }() | ||
|
|
||
| a.Action.Execute() | ||
| return recorder.failed | ||
| } | ||
|
|
||
| // attemptFailure marks an attempt that ended early because an assertion failed. | ||
| // | ||
| // The failure is raised as a panic rather than by ending the goroutine, so the | ||
| // retry loop can recover from it in place. This mirrors how the flake retry | ||
| // helper in the integration package handles the same problem. | ||
| type attemptFailure struct{} | ||
|
|
||
| // recordingT captures assertion failures instead of failing the test. | ||
| type recordingT struct { | ||
| testing.TB | ||
|
|
||
| failed bool | ||
| message string | ||
| } | ||
|
|
||
| func (t *recordingT) Errorf(format string, args ...any) { | ||
| t.fail(fmt.Sprintf(format, args...)) | ||
| } | ||
|
|
||
| func (t *recordingT) Error(args ...any) { | ||
| t.fail(fmt.Sprint(args...)) | ||
| } | ||
|
|
||
| func (t *recordingT) Fatal(args ...any) { | ||
| t.fail(fmt.Sprint(args...)) | ||
| t.FailNow() | ||
| } | ||
|
|
||
| func (t *recordingT) Fatalf(format string, args ...any) { | ||
| t.fail(fmt.Sprintf(format, args...)) | ||
| t.FailNow() | ||
| } | ||
|
|
||
| func (t *recordingT) Fail() { | ||
| t.fail("") | ||
| } | ||
|
|
||
| func (t *recordingT) FailNow() { | ||
| t.failed = true | ||
| panic(attemptFailure{}) | ||
| } | ||
|
|
||
| func (t *recordingT) Failed() bool { | ||
| return t.failed | ||
| } | ||
|
|
||
| func (t *recordingT) fail(message string) { | ||
| t.failed = true | ||
| if message != "" { | ||
| t.message = message | ||
| } | ||
| } | ||
|
|
||
| // NewEventually returns an [Eventually] wrapping the given action. | ||
| func NewEventually(action Action) *Eventually { | ||
| return &Eventually{Action: action} | ||
| } | ||
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.
🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift
Do not execute a release binary fetched over plain HTTP.
Line 395 states that this job downloads the executable over plain HTTP. A network attacker can replace that binary and run code on the CI runner.
Set explicit least-privilege
permissions, setpersist-credentials: falseon both checkout steps, and fetch the binary through TLS with a pinned checksum or signature verification.🧰 Tools
🪛 zizmor (1.29.0)
[warning] 411-412: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[warning] 438-439: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[warning] 396-421: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
🤖 Prompt for AI Agents
Source: Linters/SAST tools