feat: expose transaction failure stages and conservative outcomes

This commit is contained in:
2026-09-26 20:29:45 +08:00
parent 8eddef9cc8
commit 7a177744ab
25 changed files with 1171 additions and 44 deletions

View File

@@ -20,7 +20,7 @@ func TestGeneratedMySQLConfigurationAndLifecycle(t *testing.T) {
t.Fatal(err)
}
if cfg.Database.User != "omni_test" || cfg.Database.TLS.ServerName != "localhost" || cfg.Database.Pool.MaxOpenConns != 2 {
t.Fatal("structured environment overlay was not applied")
t.Fatal("explicit database configuration references were not applied")
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

View File

@@ -72,7 +72,7 @@ def main():
path.chmod(0o644) # Disposable fixtures, readable by the container's mysql uid.
project = temp / "consumer"
project.mkdir()
version = args.version or "v0.15.0-preview"
version = args.version or re.search(r'const currentVersion = "([^"]+)"', (ROOT / "cmd/go-omni/apigen/generate.go").read_text()).group(1)
(project / "go.mod").write_text(f"module example.com/connectorAcceptance\n\ngo 1.22.0\n\nrequire {MODULE} {version}\n")
if args.local:
run(["go", "mod", "edit", f"-replace={MODULE}={ROOT}"], project, env)
@@ -155,6 +155,8 @@ def main():
events = [json.loads(line) for line in output.splitlines() if line.startswith("{")]
passed = [e["Test"] for e in events if e.get("Action") == "pass" and "Test" in e]
assert "TestMySQLConnectorTLSAndPoolIsolation" in passed
assert "TestMySQLConnectorTLSAndPoolIsolation/commitAcknowledgementLost" in passed
assert "TestMySQLConnectorTLSAndPoolIsolation/rollbackAcknowledgementLost" in passed
assert "TestStructuredMySQLAdapterTLS" in passed
if args.gorm:
assert "TestGORMMySQLTransactionsCancellationAndLogs" in passed
@@ -164,6 +166,18 @@ def main():
GO_OMNI_DB_DATABASE="omni_test", GO_OMNI_DB_TLS_CA_FILE=str(certs / "ca.pem"),
GO_OMNI_DB_TLS_SERVER_NAME="localhost", GO_OMNI_DB_POOL_MAX_OPEN_CONNS="2",
GO_OMNI_DB_POOL_MAX_IDLE_CONNS="2")
# Since v0.22, configuration reads only explicitly declared references.
# JSON is valid YAML; keep secrets in the temporary process environment.
(generated / "etc/config.yaml").write_text(json.dumps({
"server": {"log": {"output": "discard"}},
"database": {
"host": "${GO_OMNI_DB_HOST}", "port": "${GO_OMNI_DB_PORT}",
"user": "${GO_OMNI_DB_USER}", "password": "${GO_OMNI_DB_PASSWORD}",
"database": "${GO_OMNI_DB_DATABASE}",
"tls": {"caFile": "${GO_OMNI_DB_TLS_CA_FILE}", "serverName": "${GO_OMNI_DB_TLS_SERVER_NAME}"},
"pool": {"maxOpenConns": "${GO_OMNI_DB_POOL_MAX_OPEN_CONNS}", "maxIdleConns": "${GO_OMNI_DB_POOL_MAX_IDLE_CONNS}"},
},
}, indent=2) + "\n")
print("Testing generated MySQL service config, independent pools and cleanup", flush=True)
check_output = run([cli, "api", "generate", "--check"], generated, env)
if not args.local:
@@ -182,7 +196,7 @@ def main():
finally:
if created:
run(["docker", "rm", "--force", "--volumes", name], temp, env)
report_sources = sources + ["scripts/verifyConnector.py", "scripts/testdata/mysqlGenerated_test.go"]
report_sources = sources + ["scripts/verifyConnector.py", "scripts/testdata/mysqlGenerated_test.go", "database/transaction.go"]
if args.phases:
report_sources += ["scripts/testdata/phasedMySQL_test.go", "lifecycle/runtime.go", "lifecycle/phases.go"]
if args.gorm:

View File

@@ -163,6 +163,7 @@ def main():
"logx": {"TestLoggerTimeZoneIsolation", "TestLoggerTimeZoneValidationAndDST", "TestLoggerTimeZoneConcurrentChildren"},
"cors": {"TestPolicyPreflightAndActual", "TestPolicyValidation", "TestPolicyCredentialsWildcardAndDuplicates", "TestPolicyPreservesHTTPAndSnapshot"},
"app": {"TestAppCORSRoutingAndErrors", "TestAppCORSConfigIsolation", "TestCORSTransports"},
"database": {"TestInTxStagesAndCauses", "TestInTxCommitOutcomeUnknown", "TestInTxCancellationAndOwnership", "TestInTxCancellationRace", "TestInTxSuccessPanicAndConcurrency", "TestInTxPublicErrorCompatibility"},
}.items():
output = run(["go", "test", "-race", "-count=1", "-json", MODULE + "/" + package], starter, env)
events = [json.loads(line) for line in output.splitlines() if line.startswith("{")]
@@ -171,6 +172,12 @@ def main():
checks += ["remotePreciseModuleDiagnostics", "remoteJSONSuccessStatuses", "remoteRejectedStatusBodyCleanup",
"remoteCopyTypePreflight", "remoteEmptyCollectionTypeRejection", "remoteLoggerTimeZoneIsolation",
"remoteCORSPolicyRace", "remoteCORSErrorResponses", "remoteCORSTransports", "generatedCORSConfigOnly"]
transaction_output = run(["go", "run", MODULE + "/examples/transactions"], starter, env)
assert "commitAcknowledgementLost: stage=commit outcome=unknown stored=true cleanup=false" in transaction_output
assert "workFailure: stage=work outcome=rolledBack stored=false cleanup=true" in transaction_output
checks += ["remoteTransactionStages", "remoteTransactionCauseChains", "remoteCommitOutcomeUnknown",
"remoteCancellationOutcomeUnknown", "remoteTransactionPanicAndConcurrency",
"remoteTransactionPublicErrors", "remoteTransactionExample"]
checks += ["flatFieldComposition", "copyxThreeEndpointConsumer", "explicitNullAndMoneyPolicies", "fieldAdditionWithoutCopyRuleChanges", "middlewareFactoryInjectionAndCleanup", "optionalRepositoryPreservesBusiness"]
@@ -193,6 +200,7 @@ def main():
sources += [str(p.relative_to(ROOT)) for p in (ROOT / "examples/developerTools").iterdir() if p.is_file()]
sources += [str(p.relative_to(ROOT)) for p in (ROOT / "cors").glob("*.go")]
sources += ["app/cors_test.go", "cmd/go-omni/apigen/corsConsumer_test.go", "cmd/go-omni/apigen/testdata/cors/consumer_test.go", "examples/cors/main.go", "examples/cors/config.yaml", "scripts/corsBrowser/main.go"]
sources += ["database/transaction.go", "database/transaction_test.go", "database/transactionResponse_test.go", "database/mysqlConnector_test.go", "examples/transactions/main.go", "examples/transactions/demoDriver.go"]
report = {"passed": True, "frameworkVersion": args.version, "frameworkSum": dependency["Sum"],
"freshModuleCache": True, "noLocalReplace": True,
"durationSeconds": round(time.monotonic() - started, 2), "checks": checks,