diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml new file mode 100644 index 0000000..10872b5 --- /dev/null +++ b/.github/workflows/e2e.yaml @@ -0,0 +1,80 @@ +name: Run E2E Test Suites + +on: + push: + branches: + - main + - release/** + pull_request: + branches: + - main + - release/** +permissions: read-all +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true +jobs: + changes: + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + src: ${{ steps.filter.outputs.src }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - uses: dorny/paths-filter@v2 + id: filter + with: + token: ${{ secrets.GITHUB_TOKEN }} + filters: | + src: + - '*.go' + - '**/*.go' + - 'go.mod' + - 'go.sum' + - 'Makefile' + - '.github/**' + - 'internal/**' + - 'svc/**' + - 'core/**' + run-test: + needs: changes + if: | + (needs.changes.outputs.src == 'true') + runs-on: ubuntu-latest + timeout-minutes: 20 + services: + etcd: + image: bitnami/etcd:3.4.0 + ports: + - 2379:2379 + - 2380:2380 + env: + ALLOW_NONE_AUTHENTICATION: yes + ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379 + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - name: Setup Go Environment + uses: actions/setup-go@v3 + with: + go-version: '1.20.3' + + - name: Install ginkgo + run: | + go install github.com/onsi/ginkgo/v2/ginkgo@latest + sudo cp ~/go/bin/ginkgo /usr/local/bin + + - name: Run E2E Test Suites + working-directory: ./ + run: | + export PATH=$PWD/bin:$PATH + make build + make e2e diff --git a/.gitignore b/.gitignore index 5ed72c2..e8dee50 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ .vscode/ .DS_Store -go.sum \ No newline at end of file +go.sum + +bin/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3925e92 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +#/bin/bash +PWD ?= $(shell pwd) +export PATH := $(PWD)/bin:$(PATH) + +default: help +help: ## Display this help + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) +.PHONY: help + +build: build-main build-notify ## Build main and notify +.PHONY: build + +build-main: + @go build -o bin/main main.go +.PHONY: build-main + +build-notify: + @go build -o bin/notify notify/main.go +.PHONY: build-notify + +e2e: ## Run e2e test + @cd test/e2e && ginkgo -r +.PHONY: e2e + +.PHONY: fmt +fmt: ## Format all go codes + ./scripts/goimports-reviser.sh diff --git a/core/etcd.go b/core/etcd.go index 04a3eaf..a016174 100644 --- a/core/etcd.go +++ b/core/etcd.go @@ -75,7 +75,7 @@ func (e *EtcdMgr) Close() { _ = e.watcher.Close() } -//CreateWatchChan 创建 watch 通道 +// CreateWatchChan 创建 watch 通道 func (e *EtcdMgr) CreateWatchChan(ctx context.Context, key string) (clientv3.WatchChan, error) { // constant.ChannelConfigDir getResp, err := e.kv.Get(ctx, key, clientv3.WithPrefix()) diff --git a/go.mod b/go.mod index b08184d..8450fa0 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,33 @@ module broadcast -go 1.16 +go 1.20 require ( + github.com/onsi/ginkgo/v2 v2.11.0 + github.com/onsi/gomega v1.27.10 go.etcd.io/etcd/api/v3 v3.5.0 go.etcd.io/etcd/client/v3 v3.5.0 google.golang.org/grpc v1.38.0 ) + +require ( + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.17.0 // indirect + golang.org/x/net v0.12.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect + golang.org/x/tools v0.9.3 // indirect + google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/scripts/goimports-reviser.sh b/scripts/goimports-reviser.sh old mode 100644 new mode 100755 diff --git a/svc/broadcast.go b/svc/broadcast.go index 225d902..a29fb5b 100644 --- a/svc/broadcast.go +++ b/svc/broadcast.go @@ -60,7 +60,7 @@ type RegionBroad struct { stopC chan struct{} } -//trimPrefix 过滤出对应变动的 regionId +// trimPrefix 过滤出对应变动的 regionId func (r *RegionBroad) trimPrefix(key string) RegionId { return RegionId(strings.TrimPrefix(key, r.prefix)) } diff --git a/test/e2e/broadcast/broadcast.go b/test/e2e/broadcast/broadcast.go new file mode 100644 index 0000000..638a6c2 --- /dev/null +++ b/test/e2e/broadcast/broadcast.go @@ -0,0 +1,75 @@ +/* + * MIT License + * + * Copyright (c) 2021 ashing + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package broadcast + +import ( + "bytes" + "os/exec" + "syscall" + "time" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +var _ = ginkgo.Describe("CLI", func() { + ginkgo.Context("test add agent and then notify", func() { + ginkgo.It("should return logs", func() { + // start main + var out bytes.Buffer + cmd := exec.Command("main") + cmd.Stdout = &out + cmd.Stderr = &out + err := cmd.Start() + gomega.Expect(err).Should(gomega.BeNil()) + pid := cmd.Process.Pid + gomega.Expect(pid).Should(gomega.BeNumerically(">", 0)) + go func() { + time.Sleep(3 * time.Second) + err = cmd.Process.Signal(syscall.SIGINT) + gomega.Expect(err).Should(gomega.BeNil()) + err = cmd.Wait() + gomega.Expect(err).Should(gomega.BeNil()) + }() + // start notify + var notifyOut bytes.Buffer + notifyCmd := exec.Command("notify") + notifyCmd.Stdout = ¬ifyOut + notifyCmd.Stderr = ¬ifyOut + err = notifyCmd.Run() + gomega.Expect(err).Should(gomega.BeNil()) + // check log + time.Sleep(5 * time.Second) + gomega.Expect(out.String()).Should(gomega.ContainSubstring("add agent agent_1 region_123456")) + gomega.Expect(out.String()).Should(gomega.ContainSubstring("add agent agent_2 region_123456")) + gomega.Expect(out.String()).Should(gomega.ContainSubstring("key: /config/region_123456 value: value change")) + gomega.Expect(out.String()).Should(gomega.ContainSubstring("agents count 2")) + gomega.Expect(out.String()).Should(gomega.ContainSubstring("agent_1 接收到新的配置,value change")) + gomega.Expect(out.String()).Should(gomega.ContainSubstring("agent_2 接收到新的配置,value change")) + + gomega.Expect(notifyOut.String()).Should(gomega.ContainSubstring("value will change")) + }) + }) +}) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go new file mode 100644 index 0000000..27a0d46 --- /dev/null +++ b/test/e2e/e2e_test.go @@ -0,0 +1,39 @@ +/* + * MIT License + * + * Copyright (c) 2021 ashing + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package e2e + +import ( + "testing" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + + _ "broadcast/test/e2e/broadcast" +) + +func TestBroadCast(t *testing.T) { + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "BroadCast CLI test suites") +}