Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,17 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'

- name: Cache Go modules
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
cache-dependency-path: '**/go.sum'

- name: Install dependencies
run: go mod tidy
run: go mod download

- name: Build
run: go build -v ./...

- name: Run tests with coverage
run: go test ./... -v -race -coverprofile=coverage.out -covermode=atomic
Expand Down
19 changes: 11 additions & 8 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,15 @@ func (c *Client) Connect() error {
c.log.Sugar().Errorf("Error connecting: %v\n", err)
return errors.New("failed to connect")
}
if code == http.StatusOK {
switch code {
case http.StatusOK:
c.log.Info("Connected successfully")
c.initHeartbeat()
return nil
} else if code == http.StatusConflict {
case http.StatusConflict:
c.log.Warn("Connection exists at another device")
return errors.New("failed to connect")
} else {
default:
c.log.Error("Failed to connect")
return errors.New("failed to connect")
}
Expand All @@ -61,14 +62,15 @@ func (c *Client) Disconnect() error {
c.log.Sugar().Errorf("Error disconnecting: %v\n", err)
return errors.New("failed to disconnect")
}

if code == http.StatusOK {
c.log.Info("Disconnected successfully")
c.stopHeartbeat()
return nil
} else {
c.log.Error("Failed to disconnect")
return errors.New("failed to disconnect")
}

c.log.Error("Failed to disconnect")
return errors.New("failed to disconnect")
}

func (c *Client) heartbeat() {
Expand All @@ -77,12 +79,13 @@ func (c *Client) heartbeat() {
c.log.Sugar().Errorf("Error sending heartbeat: %v\n", err)
return
}

if code == http.StatusOK {
c.log.Info("Heartbeat sent successfully")
} else {
c.log.Error("Failed to send heartbeat")
return
}

c.log.Error("Failed to send heartbeat")
}

func (c *Client) initHeartbeat() {
Expand Down
213 changes: 99 additions & 114 deletions internal/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,121 +11,106 @@ import (
)

func TestConnect(t *testing.T) {
tests := []struct {
Name string
ReceivedResponse int
IsError bool
Heartbeat bool
}{
{
Name: "Server returns 500",
ReceivedResponse: http.StatusInternalServerError,
IsError: true,
Heartbeat: false,
},
{
Name: "Connection already exists",
ReceivedResponse: http.StatusConflict,
IsError: true,
Heartbeat: false,
},
{
Name: "Connected successfully",
ReceivedResponse: http.StatusOK,
IsError: false,
Heartbeat: true,
},
}

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
t.Parallel()
log := zap.NewNop()

server := newMockServer(test.ReceivedResponse, http.StatusOK)
defer server.Server.Close()

client := New(Config{
Host: server.Server.URL,
UserID: "u1",
DeviceID: "d1",
Log: log,
})

err := client.Connect()
if test.IsError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}

time.Sleep(2 * time.Second)

server.mu.Lock()
if test.Heartbeat {
assert.GreaterOrEqual(t, server.HeartbeatCount, 1)
} else {
assert.Zero(t, server.HeartbeatCount)
}
server.mu.Unlock()
})
}
t.Run("Successful connect", func(t *testing.T) {
t.Parallel()

server := newMockServer(http.StatusOK, http.StatusOK)
defer server.Server.Close()

client := newClient(server.Server.URL)

err := client.Connect()
assert.NoError(t, err)

time.Sleep(2 * time.Second)

server.mu.Lock()
assert.GreaterOrEqual(t, server.HeartbeatCount, 1)
server.mu.Unlock()
})

t.Run("Connection exists", func(t *testing.T) {
t.Parallel()

server := newMockServer(http.StatusConflict, http.StatusOK)
defer server.Server.Close()

client := newClient(server.Server.URL)

err := client.Connect()
assert.Error(t, err)

time.Sleep(2 * time.Second)

server.mu.Lock()
assert.Zero(t, server.HeartbeatCount)
server.mu.Unlock()
})

t.Run("Internal server error", func(t *testing.T) {
t.Parallel()

server := newMockServer(http.StatusInternalServerError, http.StatusOK)
defer server.Server.Close()

client := newClient(server.Server.URL)

err := client.Connect()
assert.Error(t, err)

time.Sleep(2 * time.Second)

server.mu.Lock()
assert.Zero(t, server.HeartbeatCount)
server.mu.Unlock()
})
}

func TestDisconnect(t *testing.T) {
tests := []struct {
Name string
ReceivedResponse int
IsError bool
Heartbeat bool
}{
{
Name: "Success",
ReceivedResponse: http.StatusOK,
IsError: false,
Heartbeat: false,
},
{
Name: "Fail",
ReceivedResponse: http.StatusInternalServerError,
IsError: true,
Heartbeat: true,
},
}

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
t.Parallel()

log := zap.NewNop()

server := newMockServer(http.StatusOK, test.ReceivedResponse)
defer server.Server.Close()

client := New(Config{
Host: server.Server.URL,
UserID: "u1",
DeviceID: "d1",
Log: log,
})

client.Connect()
err := client.Disconnect()
if test.IsError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}

time.Sleep(2 * time.Second)

server.mu.Lock()
if test.Heartbeat {
assert.GreaterOrEqual(t, server.HeartbeatCount, 1)
} else {
assert.Zero(t, server.HeartbeatCount)
}
server.mu.Unlock()
})
}
t.Run("Successful disconnect", func(t *testing.T) {
t.Parallel()

server := newMockServer(http.StatusOK, http.StatusOK)
defer server.Server.Close()

client := newClient(server.Server.URL)

client.Connect()
err := client.Disconnect()
assert.NoError(t, err)

time.Sleep(2 * time.Second)

server.mu.Lock()
assert.Zero(t, server.HeartbeatCount)
server.mu.Unlock()
})

t.Run("Failed disconnect", func(t *testing.T) {
t.Parallel()

server := newMockServer(http.StatusOK, http.StatusInternalServerError)
defer server.Server.Close()

client := newClient(server.Server.URL)

client.Connect()
err := client.Disconnect()
assert.Error(t, err)

time.Sleep(2 * time.Second)

server.mu.Lock()
assert.GreaterOrEqual(t, server.HeartbeatCount, 1)
server.mu.Unlock()
})
}

func newClient(url string) *Client {
return New(Config{
Host: url,
UserID: "u1",
DeviceID: "d1",
Log: zap.NewNop(),
})
}