diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 07527aa..4b28d5e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -9,6 +9,12 @@ jobs: runs-on: ${{ matrix.os }} permissions: contents: write + services: + memcached: + image: memcached:latest + ports: + - 11211:11211 + options: --entrypoint="" strategy: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] @@ -30,7 +36,7 @@ jobs: uses: tj-actions/coverage-badge-go@v1 if: ${{ runner.os == 'linux' }} with: - green: 80 + green: 70 filename: coverage.out - uses: stefanzweifel/git-auto-commit-action@v5 diff --git a/README.md b/README.md index c3339c6..83b83af 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Gomemcached + ---
diff --git a/consistenthash/hashring_test.go b/consistenthash/hashring_test.go index 9ba672f..7d31f77 100644 --- a/consistenthash/hashring_test.go +++ b/consistenthash/hashring_test.go @@ -30,13 +30,16 @@ func BenchmarkHashRingGet(b *testing.B) { func TestHashRing_GetAllNodes(t *testing.T) { ch := NewHashRing() + allNodes := ch.GetAllNodes() + assert.Nil(t, allNodes, "GetAllNodes: without added nodes") + for i := 0; i < keySize; i++ { ch.Add("localhost:" + strconv.Itoa(i)) } count := ch.GetNodesCount() assert.Equalf(t, keySize, count, "GetNodesCount: have - %d; want - %d", count, keySize) - allNodes := ch.GetAllNodes() + allNodes = ch.GetAllNodes() assert.Equal(t, keySize, len(allNodes)) for i := 0; i < keySize; i++ { diff --git a/memcached/constants_test.go b/memcached/constants_test.go index d25c065..4ea6a4e 100644 --- a/memcached/constants_test.go +++ b/memcached/constants_test.go @@ -4,6 +4,8 @@ package memcached import ( "strings" "testing" + + "github.com/stretchr/testify/assert" ) func TestCommandCodeString(t *testing.T) { @@ -38,3 +40,113 @@ func TestIsQuiet(t *testing.T) { } } } + +func Test_prepareAuthData(t *testing.T) { + type args struct { + user string + pass string + } + tests := []struct { + name string + args args + want []byte + }{ + { + name: "1", args: args{ + user: "testuser", + pass: "testpass", + }, + want: []byte("\x00testuser\x00testpass"), + }, + { + name: "2", args: args{ + user: "anotheruser", + pass: "anotherpass", + }, + want: []byte("\x00anotheruser\x00anotherpass"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, prepareAuthData(tt.args.user, tt.args.pass), "prepareAuthData(%v, %v)", tt.args.user, tt.args.pass) + }) + } +} + +func TestOpCode_changeOnQuiet(t *testing.T) { + type args struct { + def OpCode + } + tests := []struct { + name string + o OpCode + args args + want OpCode + }{ + { + name: GET.String(), + o: GET, + args: args{def: GETQ}, + want: GETQ, + }, + { + name: SET.String(), + o: SET, + args: args{def: GETQ}, + want: SETQ, + }, + { + name: ADD.String(), + o: ADD, + args: args{def: GETQ}, + want: ADDQ, + }, + { + name: REPLACE.String(), + o: REPLACE, + args: args{def: GETQ}, + want: REPLACEQ, + }, + { + name: DELETE.String(), + o: DELETE, + args: args{def: GETQ}, + want: DELETEQ, + }, + { + name: INCREMENT.String(), + o: INCREMENT, + args: args{def: GETQ}, + want: INCREMENTQ, + }, + { + name: DECREMENT.String(), + o: DECREMENT, + args: args{def: GETQ}, + want: DECREMENTQ, + }, + { + name: FLUSH.String(), + o: FLUSH, + args: args{def: GETQ}, + want: FLUSHQ, + }, + { + name: APPEND.String(), + o: APPEND, + args: args{def: GETQ}, + want: APPENDQ, + }, + { + name: PREPEND.String(), + o: PREPEND, + args: args{def: GETQ}, + want: PREPENDQ, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, tt.o.changeOnQuiet(tt.args.def), "changeOnQuiet(%v)", tt.args.def) + }) + } +} diff --git a/memcached/options_test.go b/memcached/options_test.go new file mode 100644 index 0000000..23588cb --- /dev/null +++ b/memcached/options_test.go @@ -0,0 +1,50 @@ +package memcached + +import ( + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/aliexpressru/gomemcached/consistenthash" +) + +func TestWithOptions(t *testing.T) { + const ( + maxIdleConns = 10 + disable = true + enable + authUser = "admin" + authPass = "password" + timeout = 5 * time.Second + period = time.Second + ) + + hr := consistenthash.NewCustomHashRing(1, nil) + os.Setenv("MEMCACHED_SERVERS", "localhost:11211") + mcl, _ := InitFromEnv( + WithMaxIdleConns(maxIdleConns), + WithTimeout(timeout), + WithCustomHashRing(hr), + WithPeriodForNodeHealthCheck(period), + WithPeriodForRebuildingNodes(period), + WithDisableNodeProvider(), + WithDisableRefreshConnsInPool(), + WithDisableMemcachedDiagnostic(), + WithAuthentication(authUser, authPass), + ) + t.Cleanup(func() { + mcl.CloseAllConns() + }) + + assert.Equal(t, maxIdleConns, mcl.maxIdleConns, "WithMaxIdleConns should set maxIdleConns") + assert.Equal(t, timeout, mcl.timeout, "WithTimeout should set timeout") + assert.Equal(t, hr, mcl.hr, "WithCustomHashRing should set hr") + assert.Equal(t, period, mcl.nodeHCPeriod, "WithPeriodForNodeHealthCheck should set period") + assert.Equal(t, period, mcl.nodeRBPeriod, "WithPeriodForRebuildingNodes should set period") + assert.Equal(t, disable, mcl.disableNodeProvider, "WithDisableNodeProvider should set disable") + assert.Equal(t, disable, mcl.disableRefreshConns, "WithDisableRefreshConnsInPool should set disable") + assert.Equal(t, disable, mcl.disableMemcachedDiagnostic, "WithDisableMemcachedDiagnostic should set disable") + assert.Equal(t, enable, mcl.authEnable, "WithAuthentication should set enable") +} diff --git a/memcached/requests_test.go b/memcached/requests_test.go index 5812c21..2ae4a0f 100644 --- a/memcached/requests_test.go +++ b/memcached/requests_test.go @@ -364,3 +364,90 @@ func BenchmarkReceiveRequestNoBuf(b *testing.B) { } } } + +func TestRequest_prepareExtras(t *testing.T) { + type fields struct { + Opcode OpCode + } + type args struct { + expiration uint32 + delta uint64 + initVal uint64 + } + tests := []struct { + name string + fields fields + args args + expect []byte + }{ + { + name: "GET must not have extras", + fields: fields{ + Opcode: GET, + }, + args: args{ + expiration: 256, + delta: 1, + initVal: 1, + }, + expect: nil, + }, + { + name: "SET", + fields: fields{ + Opcode: SET, + }, + args: args{ + expiration: 256, + delta: 1, + initVal: 1, + }, + expect: []byte{ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, + }, + }, + { + name: "INCREMENT", + fields: fields{ + Opcode: INCREMENT, + }, + args: args{ + expiration: 256, + delta: 1, + initVal: 42, + }, + expect: []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, + 0x00, 0x00, 0x01, 0x00, + }, + }, + { + name: "FLUSH", + fields: fields{ + Opcode: FLUSH, + }, + args: args{ + expiration: 256, + delta: 0, + initVal: 0, + }, + expect: []byte{ + 0x00, 0x00, 0x01, 0x00, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Request{ + Opcode: tt.fields.Opcode, + } + r.prepareExtras(tt.args.expiration, tt.args.delta, tt.args.initVal) + + if !bytes.Equal(r.Extras, tt.expect) { + t.Fatalf("Expected %#v == %#v", r.Extras, tt.expect) + } + }) + } +}