diff --git a/.test.env b/.test.env new file mode 100644 index 0000000..dcae954 --- /dev/null +++ b/.test.env @@ -0,0 +1,2 @@ +FIRESTORE_EMULATOR_HOST=localhost:8787 +MYSQL_ADDR=localhost diff --git a/Makefile b/Makefile index 6ae5a71..e3a7f8e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ include .env +include .test.env +export .PHONY: openapi openapi: openapi_http openapi_js @@ -44,4 +46,16 @@ lint: .PHONY: mycli mycli: - mycli -u ${MYSQL_USER} -p ${MYSQL_PASSWORD} ${MYSQL_DATABASE} \ No newline at end of file + mycli -u ${MYSQL_USER} -p ${MYSQL_PASSWORD} ${MYSQL_DATABASE} + +INERNAL_PACKAGES := $(wildcard internal/*) + +ifeq (test,$(firstword $(MAKECMDGOALS))) + TEST_ARGS := $(subst $$,$$$$,$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))) + $(eval $(TEST_ARGS):;@:) +endif + +.PHONY: test $(INERNAL_PACKAGES) +test: $(INERNAL_PACKAGES) +$(INERNAL_PACKAGES): + @(cd $@ && go test -count=1 -race ./... $(subst $$$$,$$,$(TEST_ARGS))) diff --git a/README.md b/README.md index 7d082e6..5f34ab8 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ No application is perfect from the beginning. With over a dozen coming articles, 5. [**Business Applications in Go: Things to know about DRY**](https://threedots.tech/post/things-to-know-about-dry/?utm_source=github.com) 6. [**When microservices in Go are not enough: introduction to DDD Lite**](https://threedots.tech/post/ddd-lite-in-go-introduction/?utm_source=github.com) 7. [**Repository pattern: painless way to simplify your Go service logic**](https://threedots.tech/post/repository-pattern-in-go/?utm_source=github.com) -8. *More articles are on the way!* +8. [**4 practical principles of high-quality database integration tests in Go**](https://threedots.tech/post/database-integration-testing/?utm_source=github.com) +9. *More articles are on the way!* ### Directories diff --git a/internal/common/go.mod b/internal/common/go.mod index 0cc3b33..7addfa1 100644 --- a/internal/common/go.mod +++ b/internal/common/go.mod @@ -3,6 +3,7 @@ module github.com/ThreeDotsLabs/wild-workouts-go-ddd-example/internal/common go 1.14 require ( + cloud.google.com/go v0.38.0 firebase.google.com/go v3.12.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-chi/chi v4.1.0+incompatible diff --git a/internal/trainer/hour_repository_test.go b/internal/trainer/hour_repository_test.go index 2bf0bb9..847e3e5 100644 --- a/internal/trainer/hour_repository_test.go +++ b/internal/trainer/hour_repository_test.go @@ -145,11 +145,14 @@ func testUpdateHour_parallel(t *testing.T, repository hour.Repository) { }) require.NoError(t, err) - workersCount := 10 + workersCount := 20 workersDone := sync.WaitGroup{} workersDone.Add(workersCount) + // closing startWorkers will unblock all workers at once, + // thanks to that it will be more likely to have race condition startWorkers := make(chan struct{}) + // if training was successfully scheduled, number of the worker is sent to this channel trainingsScheduled := make(chan int, workersCount) // we are trying to do race condition, in practice only one worker should be able to finish transaction @@ -163,9 +166,11 @@ func testUpdateHour_parallel(t *testing.T, repository hour.Repository) { schedulingTraining := false err := repository.UpdateHour(ctx, hourTime, func(h *hour.Hour) (*hour.Hour, error) { + // training is already scheduled, nothing to do there if h.HasTrainingScheduled() { return h, nil } + // training is not scheduled yet, so let's try to do that if err := h.ScheduleTraining(); err != nil { return nil, err } @@ -176,12 +181,15 @@ func testUpdateHour_parallel(t *testing.T, repository hour.Repository) { }) if schedulingTraining && err == nil { + // training is only scheduled if UpdateHour didn't return an error trainingsScheduled <- workerNum } }() } close(startWorkers) + + // we are waiting, when all workers did the job workersDone.Wait() close(trainingsScheduled)