From 20aacf0adff5d81cc7bd2de296a5302f1eae1d5b Mon Sep 17 00:00:00 2001 From: Ilya Danilov Date: Mon, 19 Jan 2026 14:23:31 +0300 Subject: [PATCH 1/5] feat: Add test --- .github/workflows/ci.yml | 154 + .golangci.yml | 70 + .pre-commit-config.yaml | 29 + CI_CD.md | 348 + Makefile | 41 + README_TESTING_CI.md | 452 + TESTING.md | 249 + TEST_SUMMARY.md | 295 + Taskfile.yml | 50 +- coverage.out | 405 + go.mod | 9 +- go.sum | 15 +- internal/api-service/usecase/link_test.go | 264 + internal/user-service/models.go | 2 +- internal/user-service/repository/user_test.go | 160 + .../user-service/transport/http/http_test.go | 228 + internal/user-service/usecase/user_test.go | 178 + tests/integration_test.go | 143 + vendor/github.com/davecgh/go-spew/LICENSE | 15 + .../github.com/davecgh/go-spew/spew/bypass.go | 145 + .../davecgh/go-spew/spew/bypasssafe.go | 38 + .../github.com/davecgh/go-spew/spew/common.go | 341 + .../github.com/davecgh/go-spew/spew/config.go | 306 + vendor/github.com/davecgh/go-spew/spew/doc.go | 211 + .../github.com/davecgh/go-spew/spew/dump.go | 509 + .../github.com/davecgh/go-spew/spew/format.go | 419 + .../github.com/davecgh/go-spew/spew/spew.go | 148 + .../github.com/mattn/go-sqlite3/.codecov.yml | 4 + vendor/github.com/mattn/go-sqlite3/.gitignore | 14 + vendor/github.com/mattn/go-sqlite3/LICENSE | 21 + vendor/github.com/mattn/go-sqlite3/README.md | 603 + vendor/github.com/mattn/go-sqlite3/backup.go | 85 + .../github.com/mattn/go-sqlite3/callback.go | 411 + vendor/github.com/mattn/go-sqlite3/convert.go | 299 + vendor/github.com/mattn/go-sqlite3/doc.go | 134 + vendor/github.com/mattn/go-sqlite3/error.go | 150 + .../mattn/go-sqlite3/sqlite3-binding.c | 256040 +++++++++++++++ .../mattn/go-sqlite3/sqlite3-binding.h | 13456 + vendor/github.com/mattn/go-sqlite3/sqlite3.go | 2281 + .../mattn/go-sqlite3/sqlite3_context.go | 103 + .../mattn/go-sqlite3/sqlite3_func_crypt.go | 120 + .../mattn/go-sqlite3/sqlite3_go18.go | 54 + .../mattn/go-sqlite3/sqlite3_libsqlite3.go | 22 + .../go-sqlite3/sqlite3_load_extension.go | 85 + .../go-sqlite3/sqlite3_load_extension_omit.go | 25 + .../sqlite3_opt_allow_uri_authority.go | 16 + .../mattn/go-sqlite3/sqlite3_opt_app_armor.go | 16 + .../go-sqlite3/sqlite3_opt_column_metadata.go | 22 + .../go-sqlite3/sqlite3_opt_foreign_keys.go | 16 + .../mattn/go-sqlite3/sqlite3_opt_fts5.go | 15 + .../mattn/go-sqlite3/sqlite3_opt_icu.go | 20 + .../go-sqlite3/sqlite3_opt_introspect.go | 16 + .../go-sqlite3/sqlite3_opt_math_functions.go | 15 + .../mattn/go-sqlite3/sqlite3_opt_os_trace.go | 15 + .../mattn/go-sqlite3/sqlite3_opt_preupdate.go | 21 + .../go-sqlite3/sqlite3_opt_preupdate_hook.go | 113 + .../go-sqlite3/sqlite3_opt_preupdate_omit.go | 22 + .../go-sqlite3/sqlite3_opt_secure_delete.go | 16 + .../sqlite3_opt_secure_delete_fast.go | 16 + .../mattn/go-sqlite3/sqlite3_opt_serialize.go | 83 + .../go-sqlite3/sqlite3_opt_serialize_omit.go | 21 + .../mattn/go-sqlite3/sqlite3_opt_stat4.go | 16 + .../go-sqlite3/sqlite3_opt_unlock_notify.c | 85 + .../go-sqlite3/sqlite3_opt_unlock_notify.go | 93 + .../mattn/go-sqlite3/sqlite3_opt_userauth.go | 295 + .../go-sqlite3/sqlite3_opt_userauth_omit.go | 158 + .../go-sqlite3/sqlite3_opt_vacuum_full.go | 16 + .../go-sqlite3/sqlite3_opt_vacuum_incr.go | 16 + .../mattn/go-sqlite3/sqlite3_opt_vtable.go | 721 + .../mattn/go-sqlite3/sqlite3_other.go | 18 + .../mattn/go-sqlite3/sqlite3_solaris.go | 15 + .../mattn/go-sqlite3/sqlite3_trace.go | 288 + .../mattn/go-sqlite3/sqlite3_type.go | 108 + .../go-sqlite3/sqlite3_usleep_windows.go | 42 + .../mattn/go-sqlite3/sqlite3_windows.go | 18 + .../github.com/mattn/go-sqlite3/sqlite3ext.h | 728 + .../mattn/go-sqlite3/static_mock.go | 38 + vendor/github.com/pmezard/go-difflib/LICENSE | 27 + .../pmezard/go-difflib/difflib/difflib.go | 772 + .../github.com/stretchr/objx/.codeclimate.yml | 21 + vendor/github.com/stretchr/objx/.gitignore | 11 + vendor/github.com/stretchr/objx/LICENSE | 22 + vendor/github.com/stretchr/objx/README.md | 80 + vendor/github.com/stretchr/objx/Taskfile.yml | 27 + vendor/github.com/stretchr/objx/accessors.go | 197 + .../github.com/stretchr/objx/conversions.go | 280 + vendor/github.com/stretchr/objx/doc.go | 66 + vendor/github.com/stretchr/objx/map.go | 214 + vendor/github.com/stretchr/objx/mutations.go | 77 + vendor/github.com/stretchr/objx/security.go | 12 + vendor/github.com/stretchr/objx/tests.go | 17 + .../github.com/stretchr/objx/type_specific.go | 346 + .../stretchr/objx/type_specific_codegen.go | 2261 + vendor/github.com/stretchr/objx/value.go | 159 + vendor/github.com/stretchr/testify/LICENSE | 21 + .../testify/assert/assertion_compare.go | 495 + .../testify/assert/assertion_format.go | 866 + .../testify/assert/assertion_format.go.tmpl | 5 + .../testify/assert/assertion_forward.go | 1723 + .../testify/assert/assertion_forward.go.tmpl | 5 + .../testify/assert/assertion_order.go | 81 + .../stretchr/testify/assert/assertions.go | 2295 + .../github.com/stretchr/testify/assert/doc.go | 50 + .../stretchr/testify/assert/errors.go | 10 + .../testify/assert/forward_assertions.go | 16 + .../testify/assert/http_assertions.go | 165 + .../testify/assert/yaml/yaml_custom.go | 24 + .../testify/assert/yaml/yaml_default.go | 36 + .../stretchr/testify/assert/yaml/yaml_fail.go | 17 + .../github.com/stretchr/testify/mock/doc.go | 44 + .../github.com/stretchr/testify/mock/mock.go | 1298 + .../stretchr/testify/require/doc.go | 31 + .../testify/require/forward_requirements.go | 16 + .../stretchr/testify/require/require.go | 2180 + .../stretchr/testify/require/require.go.tmpl | 6 + .../testify/require/require_forward.go | 1724 + .../testify/require/require_forward.go.tmpl | 5 + .../stretchr/testify/require/requirements.go | 29 + vendor/gopkg.in/yaml.v3/LICENSE | 50 + vendor/gopkg.in/yaml.v3/NOTICE | 13 + vendor/gopkg.in/yaml.v3/README.md | 150 + vendor/gopkg.in/yaml.v3/apic.go | 747 + vendor/gopkg.in/yaml.v3/decode.go | 1000 + vendor/gopkg.in/yaml.v3/emitterc.go | 2020 + vendor/gopkg.in/yaml.v3/encode.go | 577 + vendor/gopkg.in/yaml.v3/parserc.go | 1258 + vendor/gopkg.in/yaml.v3/readerc.go | 434 + vendor/gopkg.in/yaml.v3/resolve.go | 326 + vendor/gopkg.in/yaml.v3/scannerc.go | 3038 + vendor/gopkg.in/yaml.v3/sorter.go | 134 + vendor/gopkg.in/yaml.v3/writerc.go | 48 + vendor/gopkg.in/yaml.v3/yaml.go | 698 + vendor/gopkg.in/yaml.v3/yamlh.go | 807 + vendor/gopkg.in/yaml.v3/yamlprivateh.go | 198 + vendor/gorm.io/driver/sqlite/.gitignore | 1 + vendor/gorm.io/driver/sqlite/License | 21 + vendor/gorm.io/driver/sqlite/README.md | 30 + vendor/gorm.io/driver/sqlite/ddlmod.go | 285 + .../driver/sqlite/ddlmod_parse_all_columns.go | 117 + .../gorm.io/driver/sqlite/error_translator.go | 40 + vendor/gorm.io/driver/sqlite/errors.go | 7 + vendor/gorm.io/driver/sqlite/migrator.go | 430 + vendor/gorm.io/driver/sqlite/sqlite.go | 270 + vendor/gorm.io/gorm/.golangci.yml | 15 +- vendor/gorm.io/gorm/CODE_OF_CONDUCT.md | 128 + vendor/gorm.io/gorm/LICENSE | 2 +- vendor/gorm.io/gorm/callbacks/associations.go | 10 +- vendor/gorm.io/gorm/callbacks/create.go | 10 + vendor/gorm.io/gorm/callbacks/delete.go | 10 + vendor/gorm.io/gorm/callbacks/preload.go | 16 +- vendor/gorm.io/gorm/callbacks/query.go | 49 +- vendor/gorm.io/gorm/callbacks/raw.go | 5 + vendor/gorm.io/gorm/callbacks/update.go | 9 + vendor/gorm.io/gorm/chainable_api.go | 7 +- vendor/gorm.io/gorm/clause/joins.go | 32 + vendor/gorm.io/gorm/clause/returning.go | 9 +- vendor/gorm.io/gorm/finisher_api.go | 18 +- vendor/gorm.io/gorm/generics.go | 605 + vendor/gorm.io/gorm/gorm.go | 30 +- vendor/gorm.io/gorm/internal/lru/lru.go | 493 + .../gorm/internal/stmt_store/stmt_store.go | 183 + vendor/gorm.io/gorm/logger/logger.go | 12 + vendor/gorm.io/gorm/migrator/migrator.go | 4 +- vendor/gorm.io/gorm/prepare_stmt.go | 144 +- vendor/gorm.io/gorm/scan.go | 13 +- vendor/gorm.io/gorm/schema/field.go | 7 +- vendor/gorm.io/gorm/schema/index.go | 33 +- vendor/gorm.io/gorm/schema/relationship.go | 14 +- vendor/gorm.io/gorm/schema/utils.go | 2 +- vendor/gorm.io/gorm/statement.go | 57 +- vendor/modules.txt | 28 +- 171 files changed, 312390 insertions(+), 214 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .golangci.yml create mode 100644 .pre-commit-config.yaml create mode 100644 CI_CD.md create mode 100644 Makefile create mode 100644 README_TESTING_CI.md create mode 100644 TESTING.md create mode 100644 TEST_SUMMARY.md create mode 100644 coverage.out create mode 100644 internal/api-service/usecase/link_test.go create mode 100644 internal/user-service/repository/user_test.go create mode 100644 internal/user-service/transport/http/http_test.go create mode 100644 internal/user-service/usecase/user_test.go create mode 100644 tests/integration_test.go create mode 100644 vendor/github.com/davecgh/go-spew/LICENSE create mode 100644 vendor/github.com/davecgh/go-spew/spew/bypass.go create mode 100644 vendor/github.com/davecgh/go-spew/spew/bypasssafe.go create mode 100644 vendor/github.com/davecgh/go-spew/spew/common.go create mode 100644 vendor/github.com/davecgh/go-spew/spew/config.go create mode 100644 vendor/github.com/davecgh/go-spew/spew/doc.go create mode 100644 vendor/github.com/davecgh/go-spew/spew/dump.go create mode 100644 vendor/github.com/davecgh/go-spew/spew/format.go create mode 100644 vendor/github.com/davecgh/go-spew/spew/spew.go create mode 100644 vendor/github.com/mattn/go-sqlite3/.codecov.yml create mode 100644 vendor/github.com/mattn/go-sqlite3/.gitignore create mode 100644 vendor/github.com/mattn/go-sqlite3/LICENSE create mode 100644 vendor/github.com/mattn/go-sqlite3/README.md create mode 100644 vendor/github.com/mattn/go-sqlite3/backup.go create mode 100644 vendor/github.com/mattn/go-sqlite3/callback.go create mode 100644 vendor/github.com/mattn/go-sqlite3/convert.go create mode 100644 vendor/github.com/mattn/go-sqlite3/doc.go create mode 100644 vendor/github.com/mattn/go-sqlite3/error.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3-binding.c create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_context.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_func_crypt.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_go18.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_libsqlite3.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension_omit.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_allow_uri_authority.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_app_armor.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_column_metadata.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_foreign_keys.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_fts5.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_icu.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_introspect.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_math_functions.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_os_trace.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_omit.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_secure_delete.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_secure_delete_fast.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_serialize.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_serialize_omit.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_stat4.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_unlock_notify.c create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_unlock_notify.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_userauth.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_userauth_omit.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vacuum_full.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vacuum_incr.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_other.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_solaris.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_trace.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_type.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_usleep_windows.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_windows.go create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3ext.h create mode 100644 vendor/github.com/mattn/go-sqlite3/static_mock.go create mode 100644 vendor/github.com/pmezard/go-difflib/LICENSE create mode 100644 vendor/github.com/pmezard/go-difflib/difflib/difflib.go create mode 100644 vendor/github.com/stretchr/objx/.codeclimate.yml create mode 100644 vendor/github.com/stretchr/objx/.gitignore create mode 100644 vendor/github.com/stretchr/objx/LICENSE create mode 100644 vendor/github.com/stretchr/objx/README.md create mode 100644 vendor/github.com/stretchr/objx/Taskfile.yml create mode 100644 vendor/github.com/stretchr/objx/accessors.go create mode 100644 vendor/github.com/stretchr/objx/conversions.go create mode 100644 vendor/github.com/stretchr/objx/doc.go create mode 100644 vendor/github.com/stretchr/objx/map.go create mode 100644 vendor/github.com/stretchr/objx/mutations.go create mode 100644 vendor/github.com/stretchr/objx/security.go create mode 100644 vendor/github.com/stretchr/objx/tests.go create mode 100644 vendor/github.com/stretchr/objx/type_specific.go create mode 100644 vendor/github.com/stretchr/objx/type_specific_codegen.go create mode 100644 vendor/github.com/stretchr/objx/value.go create mode 100644 vendor/github.com/stretchr/testify/LICENSE create mode 100644 vendor/github.com/stretchr/testify/assert/assertion_compare.go create mode 100644 vendor/github.com/stretchr/testify/assert/assertion_format.go create mode 100644 vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl create mode 100644 vendor/github.com/stretchr/testify/assert/assertion_forward.go create mode 100644 vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl create mode 100644 vendor/github.com/stretchr/testify/assert/assertion_order.go create mode 100644 vendor/github.com/stretchr/testify/assert/assertions.go create mode 100644 vendor/github.com/stretchr/testify/assert/doc.go create mode 100644 vendor/github.com/stretchr/testify/assert/errors.go create mode 100644 vendor/github.com/stretchr/testify/assert/forward_assertions.go create mode 100644 vendor/github.com/stretchr/testify/assert/http_assertions.go create mode 100644 vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go create mode 100644 vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go create mode 100644 vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go create mode 100644 vendor/github.com/stretchr/testify/mock/doc.go create mode 100644 vendor/github.com/stretchr/testify/mock/mock.go create mode 100644 vendor/github.com/stretchr/testify/require/doc.go create mode 100644 vendor/github.com/stretchr/testify/require/forward_requirements.go create mode 100644 vendor/github.com/stretchr/testify/require/require.go create mode 100644 vendor/github.com/stretchr/testify/require/require.go.tmpl create mode 100644 vendor/github.com/stretchr/testify/require/require_forward.go create mode 100644 vendor/github.com/stretchr/testify/require/require_forward.go.tmpl create mode 100644 vendor/github.com/stretchr/testify/require/requirements.go create mode 100644 vendor/gopkg.in/yaml.v3/LICENSE create mode 100644 vendor/gopkg.in/yaml.v3/NOTICE create mode 100644 vendor/gopkg.in/yaml.v3/README.md create mode 100644 vendor/gopkg.in/yaml.v3/apic.go create mode 100644 vendor/gopkg.in/yaml.v3/decode.go create mode 100644 vendor/gopkg.in/yaml.v3/emitterc.go create mode 100644 vendor/gopkg.in/yaml.v3/encode.go create mode 100644 vendor/gopkg.in/yaml.v3/parserc.go create mode 100644 vendor/gopkg.in/yaml.v3/readerc.go create mode 100644 vendor/gopkg.in/yaml.v3/resolve.go create mode 100644 vendor/gopkg.in/yaml.v3/scannerc.go create mode 100644 vendor/gopkg.in/yaml.v3/sorter.go create mode 100644 vendor/gopkg.in/yaml.v3/writerc.go create mode 100644 vendor/gopkg.in/yaml.v3/yaml.go create mode 100644 vendor/gopkg.in/yaml.v3/yamlh.go create mode 100644 vendor/gopkg.in/yaml.v3/yamlprivateh.go create mode 100644 vendor/gorm.io/driver/sqlite/.gitignore create mode 100644 vendor/gorm.io/driver/sqlite/License create mode 100644 vendor/gorm.io/driver/sqlite/README.md create mode 100644 vendor/gorm.io/driver/sqlite/ddlmod.go create mode 100644 vendor/gorm.io/driver/sqlite/ddlmod_parse_all_columns.go create mode 100644 vendor/gorm.io/driver/sqlite/error_translator.go create mode 100644 vendor/gorm.io/driver/sqlite/errors.go create mode 100644 vendor/gorm.io/driver/sqlite/migrator.go create mode 100644 vendor/gorm.io/driver/sqlite/sqlite.go create mode 100644 vendor/gorm.io/gorm/CODE_OF_CONDUCT.md create mode 100644 vendor/gorm.io/gorm/generics.go create mode 100644 vendor/gorm.io/gorm/internal/lru/lru.go create mode 100644 vendor/gorm.io/gorm/internal/stmt_store/stmt_store.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7083770 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,154 @@ +name: CI + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + test: + name: Test + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:16 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: linkkeeper_test + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.23' + cache: true + + - name: Install dependencies + run: | + go mod download + go install github.com/stretchr/testify/assert@latest + go install github.com/stretchr/testify/mock@latest + + - name: Run go fmt + run: | + if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then + echo "Please run 'go fmt ./...'" + gofmt -s -l . + exit 1 + fi + + - name: Run go vet + run: go vet ./... + + - name: Run tests + env: + POSTGRES_DSN: "postgres://postgres:postgres@localhost:5432/linkkeeper_test?sslmode=disable" + run: | + go test -v -race -coverprofile=coverage.out -covermode=atomic ./... + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + file: ./coverage.out + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.23' + + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v4 + with: + version: latest + args: --timeout=5m + + build: + name: Build + runs-on: ubuntu-latest + needs: [test, lint] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.23' + cache: true + + - name: Build api-service + run: go build -v -o bin/api-service ./cmd/api-service + + - name: Build user-service + run: go build -v -o bin/user-service ./cmd/user-service + + - name: Build bot-service + run: go build -v -o bin/bot-service ./cmd/bot-service + + docker: + name: Docker Build + runs-on: ubuntu-latest + needs: [test, lint] + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build api-service image + uses: docker/build-push-action@v5 + with: + context: . + file: ./build/api-service/Dockerfile + push: false + tags: linkkeeper-api-service:latest + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Build user-service image + uses: docker/build-push-action@v5 + with: + context: . + file: ./build/user-service/Dockerfile + push: false + tags: linkkeeper-user-service:latest + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Build bot-service image + uses: docker/build-push-action@v5 + with: + context: . + file: ./build/bot-service/Dockerfile + push: false + tags: linkkeeper-bot-service:latest + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..8cf4a9c --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,70 @@ +linters-settings: + govet: + check-shadowing: true + gocyclo: + min-complexity: 15 + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 2 + misspell: + locale: US + lll: + line-length: 140 + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + +linters: + disable-all: true + enable: + - bodyclose + - dogsled + - dupl + - errcheck + - exportloopref + - gochecknoinits + - goconst + - gocritic + - gocyclo + - gofmt + - goimports + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - misspell + - nakedret + - noctx + - nolintlint + - staticcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - whitespace + +issues: + exclude-rules: + - path: _test\.go + linters: + - dupl + - goconst + - gosec + - path: cmd/ + linters: + - gochecknoinits + +run: + timeout: 5m + tests: false + skip-dirs: + - vendor + - frontend diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..94d10f3 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - id: check-merge-conflict + - id: check-case-conflict + - id: detect-private-key + + - repo: https://github.com/dnephin/pre-commit-golang + rev: v0.5.1 + hooks: + - id: go-fmt + - id: go-vet + - id: go-mod-tidy + - id: go-unit-tests + args: [-timeout=30s, -short] + + - repo: local + hooks: + - id: go-test + name: go test + entry: go test -v -race -short ./... + language: system + pass_filenames: false + always_run: true diff --git a/CI_CD.md b/CI_CD.md new file mode 100644 index 0000000..16583fe --- /dev/null +++ b/CI_CD.md @@ -0,0 +1,348 @@ +# CI/CD Documentation + +This document describes the Continuous Integration and Continuous Deployment setup for the LinkKeeper project. + +## GitHub Actions Workflows + +### Main CI Pipeline (`.github/workflows/ci.yml`) + +The CI pipeline runs automatically on: +- Push to `main` and `develop` branches +- Pull requests to `main` and `develop` branches + +#### Pipeline Jobs + +##### 1. Test Job +- **Environment**: Ubuntu latest with PostgreSQL 16 +- **Steps**: + - Checkout code + - Set up Go 1.23 + - Install dependencies (including test libraries) + - Run `go fmt` check + - Run `go vet` static analysis + - Run all tests with race detector and coverage + - Upload coverage report to Codecov + +##### 2. Lint Job +- **Environment**: Ubuntu latest +- **Steps**: + - Checkout code + - Set up Go 1.23 + - Run golangci-lint with 5-minute timeout + +##### 3. Build Job +- **Dependencies**: Requires Test and Lint jobs to pass +- **Steps**: + - Checkout code + - Set up Go 1.23 + - Build all three services (api-service, user-service, bot-service) + +##### 4. Docker Job +- **Dependencies**: Requires Test and Lint jobs to pass +- **Trigger**: Only on push to `main` branch +- **Steps**: + - Checkout code + - Set up Docker Buildx + - Build Docker images for all services + - Uses GitHub Actions cache for faster builds + +## Linter Configuration + +### golangci-lint (`.golangci.yml`) + +Enabled linters: +- `bodyclose` - Checks HTTP response body is closed +- `errcheck` - Checks for unchecked errors +- `govet` - Go vet analysis +- `ineffassign` - Detects ineffectual assignments +- `staticcheck` - Static analysis checks +- `gosec` - Security checks +- `gocritic` - Comprehensive Go code analysis +- `misspell` - Spell checking +- `dupl` - Code duplication detection +- `gocyclo` - Cyclomatic complexity +- And more... + +### Settings: +- Line length limit: 140 characters +- Minimum complexity for warning: 15 +- Timeout: 5 minutes + +## Pre-commit Hooks + +### Setup (`.pre-commit-config.yaml`) + +Install hooks: +```bash +task hooks:install +# or +make install-hooks +# or +pre-commit install +``` + +### Hooks: +1. **pre-commit/pre-commit-hooks**: + - Remove trailing whitespace + - Fix end of files + - Check YAML syntax + - Detect large files + - Check for merge conflicts + - Detect private keys + +2. **dnephin/pre-commit-golang**: + - Run `go fmt` + - Run `go vet` + - Run `go mod tidy` + - Run unit tests (with 30s timeout, short mode) + +3. **Local hooks**: + - Run full test suite with race detector + +## Running CI Checks Locally + +### Quick Check +```bash +task ci:local +``` + +This will run: +1. Code formatting (`go fmt`) +2. Linting (`go vet`, `golangci-lint`) +3. All tests with coverage + +### Individual Steps + +```bash +# Format code +task fmt + +# Run linter +task lint + +# Run tests +task test + +# Run tests with coverage +task test:coverage + +# Run only unit tests +task test:unit + +# Run only integration tests +task test:integration +``` + +### Using Makefile + +```bash +# Run linter +make lint + +# Run tests +make test + +# Run tests with coverage +make test-coverage + +# Format code +make fmt +``` + +## Environment Variables + +### Required for CI/CD: + +#### GitHub Secrets +- `CODECOV_TOKEN` - (Optional) For Codecov integration + +### Local Development +- `POSTGRES_DSN` - Database connection string +- `TELEGRAM_TOKEN` - Telegram bot token +- `HTTP_ADDR` - Service HTTP address + +## Docker Build Strategy + +### Multi-stage Builds +All services use multi-stage Docker builds: +1. **Build stage**: Use `golang:latest` with full toolchain +2. **Runtime stage**: Use `debian:bookworm-slim` for smaller images + +### Build Caching +- GitHub Actions uses build cache to speed up builds +- Cache keys based on Go modules hash + +### Building Locally + +```bash +# Build specific service +docker build -f build/api-service/Dockerfile -t api-service . +docker build -f build/user-service/Dockerfile -t user-service . +docker build -f build/bot-service/Dockerfile -t bot-service . + +# Build all services +docker-compose build + +# Build with task +task docker:build +``` + +## Branch Strategy + +### Main Branches +- `main` - Production-ready code + - Full CI pipeline + Docker builds + - Should always be stable + - Protected branch (requires PR reviews) + +- `develop` - Development branch + - Full CI pipeline + - Integration of features + - Can be unstable + +### Feature Branches +- `feature/*` - New features +- `bugfix/*` - Bug fixes +- `hotfix/*` - Urgent production fixes + +### Workflow +1. Create feature branch from `develop` +2. Make changes and commit +3. Push and create PR to `develop` +4. CI checks run automatically +5. After review and CI pass, merge to `develop` +6. Periodically merge `develop` to `main` + +## Quality Gates + +### Before Merge +- ✅ All tests pass +- ✅ No linter warnings +- ✅ Code coverage maintained +- ✅ Code review approved +- ✅ No merge conflicts + +### Pre-commit (Local) +- ✅ Code formatted +- ✅ `go vet` passes +- ✅ Unit tests pass + +## Monitoring and Alerts + +### Current Setup +- GitHub Actions notifications +- Codecov coverage reports + +### Future Enhancements +- [ ] Slack/Discord notifications +- [ ] Performance regression detection +- [ ] Security vulnerability scanning +- [ ] Dependency update notifications +- [ ] Deploy previews for frontend + +## Deployment (Planned) + +### Future CD Pipeline +1. **Staging Deployment** + - Automatic deployment on merge to `develop` + - Deploy to staging environment + - Run smoke tests + +2. **Production Deployment** + - Manual approval required + - Deploy on merge to `main` + - Blue-green deployment + - Automatic rollback on failure + +3. **Container Registry** + - Push images to Docker Hub/GitHub Container Registry + - Tag with version/commit SHA + - Keep last 10 versions + +## Troubleshooting + +### CI Fails but Local Passes + +```bash +# Clean cache +go clean -cache -testcache + +# Run with same flags as CI +go test -v -race -coverprofile=coverage.out -covermode=atomic ./... + +# Check formatting +gofmt -s -l . + +# Run linter +golangci-lint run --timeout=5m +``` + +### Docker Build Fails + +```bash +# Clear Docker cache +docker system prune -a + +# Build without cache +docker-compose build --no-cache + +# Check Dockerfile syntax +docker build --check -f build/api-service/Dockerfile . +``` + +### Pre-commit Hooks Fail + +```bash +# Update hooks +pre-commit autoupdate + +# Run manually +pre-commit run --all-files + +# Skip hooks (emergency only) +git commit --no-verify +``` + +## Best Practices + +1. **Always run tests locally before pushing** +2. **Keep commits small and focused** +3. **Write meaningful commit messages** (use Conventional Commits) +4. **Don't skip CI checks** (only in emergencies) +5. **Monitor CI failures** and fix quickly +6. **Keep dependencies updated** regularly +7. **Add tests for bug fixes** before fixing +8. **Review CI logs** when tests fail + +## Commit Message Format + +Use Conventional Commits: + +``` +(): + + + +