Skip to content

Conversation

@stephanos
Copy link
Contributor

@stephanos stephanos commented Dec 1, 2025

What changed?

Changed use of testhooks's Set to be runtime error instead of compile time error.

It still ensures that the default impl (used in go build artifacts without test_dep tag) is the noop impl.

Why?

When running a functional test via plain go test it will fail, as the test_dep build flag is required. It is required to prevent accidentally using the default/noop implementation of the testhooks package which allows injecting custom behavior into production code from tests.

But requiring test_dep to run any test adds friction: CLI invocations need to specify it manually, editor setups need to be adjusted, AI agents need to be instructed. That's a high price to pay for a test helper that is used fairly sparsely.

How did you test it?

  • built
  • run locally and tested manually
  • covered by existing tests
  • added new unit test(s)
  • added new functional test(s)

Functional test without testhook:

% go test ./tests -run "OwnershipLostErrorSuite" -count=1 .
ok  	go.temporal.io/server/tests	7.062s

Functional test with testhook and build tag:

% go test ./tests -run "TestUpdateWorkflowSuite" -tags test_dep -count=1 .
ok  	go.temporal.io/server/tests	36.975s

Functional test with testhook but no build tag:

% go test ./tests -run "TestUpdateWorkflowSuite" -count=1 .
...
noop_impl.go:39: test panicked: testhooks.Set called but TestHooks are not enabled: use -tags=test_dep when running `go test`
                    goroutine 33293 [running]:
                    runtime/debug.Stack()
...
FAIL
FAIL	go.temporal.io/server/tests	57.128s
FAIL

// Set is only to be used by test code together with the test_dep build tag.
func Set[T any](th TestHooks, key Key, val T) func() {
softassert.Fail(th.logger, "testhooks.Set called without test_dep build tag")
return func() {}
Copy link
Contributor Author

@stephanos stephanos Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main change: instead of a compile time error, a runtime error for the very few tests that rely on testhooks is emitted.

Risk and impact of accidentally using it in production code are very low (ie extra logging, which should be caught in testing). But could be mitigated via linter, if needed.

Copy link
Contributor Author

@stephanos stephanos Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts from a few days later; could also consider using panic actually to make the error clearer when a user forgets to set test_dep when testing.

I wouldn't expect this function to be used in production code. Again, linter could be leveraged to make sure.

Comment on lines 53 to 65
// GetCtx gets the value of a test hook from the registry embedded in the
// context chain.
//
// TestHooks should be used sparingly, see comment on TestHooks.
func GetCtx[T any](ctx context.Context, key Key) (T, bool) {
hooks := ctx.Value(contextKey{})
if hooks, ok := hooks.(TestHooks); ok {
return Get[T](hooks, key)
}
var zero T
return zero, false
}

Copy link
Contributor Author

@stephanos stephanos Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been a while and these haven't seen any use. Safe to remove IMO.

@stephanos stephanos force-pushed the softassert-testhooks branch 4 times, most recently from 167a1cc to c4bdc6a Compare December 2, 2025 19:01
@stephanos stephanos force-pushed the softassert-testhooks branch from c4bdc6a to 81df793 Compare December 2, 2025 21:51
@stephanos stephanos marked this pull request as ready for review December 4, 2025 16:38
@stephanos stephanos requested review from a team as code owners December 4, 2025 16:38
@stephanos stephanos changed the title softassert for test hooks panic test hooks if not enabled Dec 5, 2025
@stephanos stephanos force-pushed the softassert-testhooks branch 2 times, most recently from 51b2d72 to 1ee00ab Compare December 5, 2025 20:22
@stephanos stephanos requested a review from dnr December 5, 2025 20:23
@stephanos stephanos force-pushed the softassert-testhooks branch from 1ee00ab to 7274c8a Compare December 5, 2025 20:28

var Module = fx.Options(
fx.Provide(func() (_ TestHooks) { return }),
fx.Provide(NewTestHooks),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what was wrong with the interface/concrete switch? an interface takes 16 bytes, a concrete struct{} takes 0. yeah, it may not be noticeable but why change it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was playing around with some options; the size wasn't top of mind for me here. I'll revert that, no problem.

// concerns into production code. In general you should prefer other ways of writing tests
// wherever possible, and only use TestHooks sparingly, as a last resort.
type TestHooks struct {
data *testHooksData
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could probably just be data *sync.Map?

@stephanos stephanos enabled auto-merge (squash) December 9, 2025 17:12
@stephanos stephanos merged commit 33faf7e into temporalio:main Dec 9, 2025
58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants