Skip to content

Commit

Permalink
treewide: Add tests to all Go packages and cleanup (#2)
Browse files Browse the repository at this point in the history
Test all packages, use better naming and clean up.
  • Loading branch information
kalbasit authored Nov 17, 2024
1 parent db57040 commit ee577e8
Show file tree
Hide file tree
Showing 7 changed files with 417 additions and 11 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Run all Go tests
on:
pull_request:
jobs:
go-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install devbox
uses: jetify-com/[email protected]
with:
enable-cache: 'true'

- name: Prepare Go build cache
id: go-cache-paths
run: |
devbox run -- echo "go_build_cache=$(go env GOCACHE)" >> $GITHUB_OUTPUT
devbox run -- echo "go_mod_cache=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT
# Cache go build cache, used to speedup go test
- name: Go Build Cache
uses: actions/cache@v2
with:
path: ${{ steps.go-cache-paths.outputs.go_build_cache }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}

# Cache go mod cache, used to speedup builds
- name: Go Mod Cache
uses: actions/cache@v2
with:
path: ${{ steps.go-cache-paths.outputs.go_mod_cache }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}

- name: Run the tests
if: success()
run: devbox run -- go test -v ./...
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ go.work.sum

# env file
.env

# Binaries
/main
/signal-receiver
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"net/http"
"net/url"

"github.com/kalbasit/signal-receiver/receiver"
"github.com/kalbasit/signal-receiver/server"
"github.com/kalbasit/signal-receiver/signalapireceiver"
)

var signalApiURL string
Expand Down Expand Up @@ -39,7 +39,7 @@ func main() {
uri.Path = fmt.Sprintf("/v1/receive/%s", signalAccount)
log.Printf("the fully qualified URL for signal-api was computed as %q", uri.String())

sarc, err := signalapireceiver.New(uri)
sarc, err := receiver.New(uri)
if err != nil {
panic(err)
}
Expand Down
12 changes: 11 additions & 1 deletion signalapireceiver/client.go → receiver/client.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package signalapireceiver
package receiver

import (
"encoding/json"
Expand All @@ -11,6 +11,7 @@ import (
"github.com/gorilla/websocket"
)

// Message defines the message structure received from the Signal API
type Message struct {
Envelope struct {
Source string `json:"source"`
Expand All @@ -34,13 +35,17 @@ type Message struct {
Account string `json:"account"`
}

// Client represents the Signal API client, and is returned by the New() function.
type Client struct {
*websocket.Conn

mu sync.Mutex
messages []Message
}

// New creates a new Signal API client and returns it.
// An error is returned if a websocket fails to open with the Signal's API
// /v1/receive
func New(uri *url.URL) (*Client, error) {
c, _, err := websocket.DefaultDialer.Dial(uri.String(), http.Header{})
if err != nil {
Expand All @@ -53,6 +58,9 @@ func New(uri *url.URL) (*Client, error) {
}, nil
}

// ReceiveLoop is a blocking call and it loop over receiving messages over the
// websocket and record them internally to be consumed by either Pop() or
// Flush()
func (c *Client) ReceiveLoop() {
log.Print("Starting the receive loop from Signal API")
for {
Expand All @@ -65,6 +73,7 @@ func (c *Client) ReceiveLoop() {
}
}

// Flush empties out the internal queue of messages and returns them
func (c *Client) Flush() []Message {
c.mu.Lock()
msgs := c.messages
Expand All @@ -73,6 +82,7 @@ func (c *Client) Flush() []Message {
return msgs
}

// Pop returns the oldest message in the queue or null if no message was found
func (c *Client) Pop() *Message {
c.mu.Lock()
if len(c.messages) == 0 {
Expand Down
78 changes: 78 additions & 0 deletions receiver/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package receiver

import (
"reflect"
"strconv"
"testing"
)

func TestFlush(t *testing.T) {
t.Run("returns empty list when no messages was found", func(t *testing.T) {
c := &Client{messages: []Message{}}
if want, got := []Message{}, c.Flush(); !reflect.DeepEqual(want, got) {
t.Errorf("want %#v got %#v", want, got)
}
})

t.Run("return the message if only one is there", func(t *testing.T) {
c := &Client{messages: []Message{Message{Account: "1"}}}

if want, got := []Message{Message{Account: "1"}}, c.Flush(); !reflect.DeepEqual(want, got) {
t.Errorf("want %#v got %#v", want, got)
}
})

t.Run("return messages in order", func(t *testing.T) {
c := &Client{messages: []Message{
Message{Account: "0"},
Message{Account: "1"},
Message{Account: "2"},
}}

want := []Message{
Message{Account: "0"},
Message{Account: "1"},
Message{Account: "2"},
}
got := c.Flush()
if !reflect.DeepEqual(want, got) {
t.Errorf("want\n%#v\ngot\n%#v", want, got)
}
})
}

func TestPop(t *testing.T) {
t.Run("returns null when no messages was found", func(t *testing.T) {
c := &Client{messages: []Message{}}
var want *Message
if got := c.Pop(); want != got {
t.Errorf("want %#v got %#v", want, got)
}
})

t.Run("return the message if only one is there", func(t *testing.T) {
c := &Client{messages: []Message{Message{Account: "1"}}}

want := Message{Account: "1"}
got := c.Pop()
if !reflect.DeepEqual(want, *got) {
t.Errorf("want\n%#v\ngot\n%#v", want, got)
}
})

t.Run("return messages in order", func(t *testing.T) {
c := &Client{messages: []Message{
Message{Account: "0"},
Message{Account: "1"},
Message{Account: "2"},
}}

for i := range c.messages {
want := Message{Account: strconv.Itoa(i)}
got := c.Pop()
if !reflect.DeepEqual(want, *got) {
t.Errorf("want\n%#v\ngot\n%#v", want, got)
}
}
})
}
37 changes: 29 additions & 8 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,38 @@ import (
"fmt"
"net/http"

"github.com/kalbasit/signal-receiver/signalapireceiver"
"github.com/kalbasit/signal-receiver/receiver"
)

const usage = `
GET /receive/pop => Return the oldest message
GET /receive/flush => Return all messages
`

// Server represent the HTTP server that exposes the pop/flush routes
type Server struct {
sarc *signalapireceiver.Client
sarc client
}

func New(sarc *signalapireceiver.Client) *Server {
type client interface {
Pop() *receiver.Message
Flush() []receiver.Message
}

// New returns a new Server
func New(sarc client) *Server {
return &Server{sarc: sarc}
}

// ServeHTTP implements the http.Handler interface
//
// /receive/pop
//
// This returns status 200 and a receiver.Message or status 204 with no body
//
// /receive/flush
//
// This returns status 200 and a list of receiver.Message
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusForbidden)
Expand All @@ -38,15 +54,20 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := json.NewEncoder(w).Encode(msg); err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
} else if r.URL.Path == "/receive/flush" {

return
}

if r.URL.Path == "/receive/flush" {
msgs := s.sarc.Flush()
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(msgs); err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
} else {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(fmt.Sprintf("ERROR! GET %s is not supported. The supported paths are below:", r.URL.Path) + usage))
return
}

w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(fmt.Sprintf("ERROR! GET %s is not supported. The supported paths are below:", r.URL.Path) + usage))
}
Loading

0 comments on commit ee577e8

Please sign in to comment.