Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify structure and make it more idiomatic #3

Merged
merged 20 commits into from
Oct 8, 2024
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
19 changes: 9 additions & 10 deletions .mockery.yaml
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
dir: "{{.InterfaceDirRelative}}"
inpackage: true
filename: "mock_{{.InterfaceNameSnake}}.go"
mockname: "mock{{ .InterfaceName | camelcase }}"
mock-build-tags: "!release"
disable-version-string: true
packages:
word-of-wisdom-go/pkg/api/tcp/commands:
word-of-wisdom-go/internal/app:
config:
filename: "{{.InterfaceNameSnake}}.go"
mockname: "{{ .InterfaceName | camelcase }}"
interfaces:
CommandHandler:
word-of-wisdom-go/pkg/app/challenges:
interfaces:
RequestRateMonitor:
Challenges:
word-of-wisdom-go/pkg/app/wow:
interfaces:
Query:
mockRequestRateMonitor:
mockChallenges:
mockWowQuery:
log/slog:
interfaces:
Handler:
config:
inpackage: false
outpkg: 'diag'
dir: 'pkg/diag'
dir: 'internal/diag'
filename: "mock_slog_handler.go"
mockname: MockSlogHandler
18 changes: 10 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ $(go-test-coverage):

.PHONY: $(cover_profile)
$(cover_profile): $(cover_dir)
TZ=US/Alaska go test -timeout 30s -shuffle=on -failfast -coverpkg=./pkg/...,./cmd/... -coverprofile=$(cover_profile) -covermode=atomic ./...
TZ=US/Alaska go test -shuffle=on -failfast -coverpkg=./internal/...,./cmd/... -coverprofile=$(cover_profile) -covermode=atomic ./...

test: $(go-test-coverage) $(cover_profile)
go tool cover -html=$(cover_profile) -o $(cover_html)
Expand All @@ -45,12 +45,15 @@ test: $(go-test-coverage) $(cover_profile)
docker-images:
make -C docker clean-images .local-client-image .local-server-image

$(cover_dir)/coverage.%.blob-sha:
@gh api \
$(cover_dir)/repo-name-with-owner.txt:
gh repo view --json nameWithOwner -q .nameWithOwner > $@

$(cover_dir)/coverage.%.blob-sha: $(cover_dir)/repo-name-with-owner.txt
gh api \
--method GET \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/gemyago/word-of-wisdom-go/contents/coverage/golang-coverage.$*?ref=test-artifacts \
/repos/$(shell cat $(cover_dir)/repo-name-with-owner.txt)/contents/coverage/golang-coverage.$*?ref=test-artifacts \
| jq -jr '.sha' > $@

$(cover_dir)/coverage.%.gh-cli-body.json: $(cover_dir)/coverage.% $(cover_dir)/coverage.%.blob-sha
Expand All @@ -64,7 +67,6 @@ $(cover_dir)/coverage.%.gh-cli-body.json: $(cover_dir)/coverage.% $(cover_dir)/c
@base64 -i $< | tr -d '\n' >> $@
@printf "\"\n}">> $@


# Orphan branch will need to be created prior to running this
# git checkout --orphan test-artifacts
# git rm -rf .
Expand All @@ -74,16 +76,16 @@ $(cover_dir)/coverage.%.gh-cli-body.json: $(cover_dir)/coverage.% $(cover_dir)/c
# git commit -m 'init'
# git push origin test-artifacts
.PHONY: push-test-artifacts
push-test-artifacts: $(cover_dir)/coverage.svg.gh-cli-body.json $(cover_dir)/coverage.html.gh-cli-body.json
push-test-artifacts: $(cover_dir)/coverage.svg.gh-cli-body.json $(cover_dir)/coverage.html.gh-cli-body.json $(cover_dir)/repo-name-with-owner.txt
@gh api \
--method PUT \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/gemyago/word-of-wisdom-go/contents/coverage/golang-coverage.svg \
/repos/$(shell cat $(cover_dir)/repo-name-with-owner.txt)/contents/coverage/golang-coverage.svg \
--input $(cover_dir)/coverage.svg.gh-cli-body.json
@gh api \
--method PUT \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/gemyago/word-of-wisdom-go/contents/coverage/golang-coverage.html \
/repos/$(shell cat $(cover_dir)/repo-name-with-owner.txt)/contents/coverage/golang-coverage.html \
--input $(cover_dir)/coverage.html.gh-cli-body.json
18 changes: 14 additions & 4 deletions cmd/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"log/slog"
"os"
"time"
"word-of-wisdom-go/pkg/diag"
"word-of-wisdom-go/internal/diag"

"github.com/spf13/cobra"
"go.uber.org/dig"
Expand All @@ -30,6 +30,15 @@ type runWOWCommandParams struct {
output io.Writer
}

func writeLines(w io.Writer, lines ...string) error {
for _, line := range lines {
if _, err := fmt.Fprintln(w, line); err != nil {
return fmt.Errorf("failed to write line: %w", err)
}
}
return nil
}

func runWOWCommand(ctx context.Context, params runWOWCommandParams) error {
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(params.MaxSessionDuration))
defer cancel()
Expand All @@ -50,9 +59,10 @@ func runWOWCommand(ctx context.Context, params runWOWCommandParams) error {
if err != nil {
return err
}
fmt.Fprintln(params.output, "Your Word of Wisdom for today:")
fmt.Fprintln(params.output, result)
return nil
return writeLines(params.output,
"Your Word of Wisdom for today:",
result,
)
}

func newClientCmd(container *dig.Container) *cobra.Command {
Expand Down
28 changes: 14 additions & 14 deletions cmd/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"context"
"errors"
"testing"
"word-of-wisdom-go/pkg/diag"
"word-of-wisdom-go/pkg/services/networking"
"word-of-wisdom-go/internal/diag"
"word-of-wisdom-go/internal/services"

"github.com/go-faker/faker/v4"
"github.com/stretchr/testify/assert"
Expand All @@ -17,20 +17,20 @@ func TestClient(t *testing.T) {
t.Run("runWOWCommand", func(t *testing.T) {
t.Run("should process wow command", func(t *testing.T) {
ctx := context.Background()
mockSession := networking.NewMockSession()
ctrl := services.NewMockSessionIOController()
wantWow := faker.Sentence()
var output bytes.Buffer
wantAddress := faker.Word()
params := runWOWCommandParams{
serverAddress: wantAddress,
RootLogger: diag.RootTestLogger(),
SessionDialer: sessionDialerFunc(func(network, address string) (networking.Session, func() error, error) {
SessionDialer: sessionDialerFunc(func(network, address string) (*services.SessionIO, func() error, error) {
assert.Equal(t, "tcp", network)
assert.Equal(t, wantAddress, address)
return mockSession, func() error { return nil }, nil
return ctrl.Session, func() error { return nil }, nil
}),
WOWCommand: WOWCommandFunc(func(_ context.Context, session networking.Session) (string, error) {
assert.Equal(t, mockSession, session)
WOWCommand: WOWCommandFunc(func(_ context.Context, session *services.SessionIO) (string, error) {
assert.Equal(t, ctrl.Session, session)
return wantWow, nil
}),
output: &output,
Expand All @@ -45,7 +45,7 @@ func TestClient(t *testing.T) {
params := runWOWCommandParams{
serverAddress: wantAddress,
RootLogger: diag.RootTestLogger(),
SessionDialer: sessionDialerFunc(func(_, _ string) (networking.Session, func() error, error) {
SessionDialer: sessionDialerFunc(func(_, _ string) (*services.SessionIO, func() error, error) {
return nil, nil, wantDialErr
}),
}
Expand All @@ -57,10 +57,10 @@ func TestClient(t *testing.T) {
params := runWOWCommandParams{
serverAddress: faker.Word(),
RootLogger: diag.RootTestLogger(),
SessionDialer: sessionDialerFunc(func(_, _ string) (networking.Session, func() error, error) {
return networking.NewMockSession(), func() error { return nil }, nil
SessionDialer: sessionDialerFunc(func(_, _ string) (*services.SessionIO, func() error, error) {
return services.NewMockSessionIOController().Session, func() error { return nil }, nil
}),
WOWCommand: WOWCommandFunc(func(_ context.Context, _ networking.Session) (string, error) {
WOWCommand: WOWCommandFunc(func(_ context.Context, _ *services.SessionIO) (string, error) {
return "", wantErr
}),
}
Expand All @@ -71,10 +71,10 @@ func TestClient(t *testing.T) {
params := runWOWCommandParams{
serverAddress: faker.Word(),
RootLogger: diag.RootTestLogger(),
SessionDialer: sessionDialerFunc(func(_, _ string) (networking.Session, func() error, error) {
return networking.NewMockSession(), func() error { return errors.New(faker.Sentence()) }, nil
SessionDialer: sessionDialerFunc(func(_, _ string) (*services.SessionIO, func() error, error) {
return services.NewMockSessionIOController().Session, func() error { return errors.New(faker.Sentence()) }, nil
}),
WOWCommand: WOWCommandFunc(func(_ context.Context, _ networking.Session) (string, error) {
WOWCommand: WOWCommandFunc(func(_ context.Context, _ *services.SessionIO) (string, error) {
return faker.Sentence(), nil
}),
output: &bytes.Buffer{},
Expand Down
12 changes: 6 additions & 6 deletions cmd/client/dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ import (
"fmt"
"net"
"time"
"word-of-wisdom-go/pkg/services/networking"
"word-of-wisdom-go/internal/services"

"go.uber.org/dig"
)

type SessionDialer interface {
// DialSession establishes new connection and returns session and close function
DialSession(network, address string) (networking.Session, func() error, error)
DialSession(network, address string) (*services.SessionIO, func() error, error)
}

type sessionDialerFunc func(network, address string) (networking.Session, func() error, error)
type sessionDialerFunc func(network, address string) (*services.SessionIO, func() error, error)

func (f sessionDialerFunc) DialSession(network, address string) (networking.Session, func() error, error) {
func (f sessionDialerFunc) DialSession(network, address string) (*services.SessionIO, func() error, error) {
return f(network, address)
}

Expand All @@ -30,15 +30,15 @@ type SessionDialerDeps struct {
}

func newSessionDialer(deps SessionDialerDeps) SessionDialer {
return sessionDialerFunc(func(network, address string) (networking.Session, func() error, error) {
return sessionDialerFunc(func(network, address string) (*services.SessionIO, func() error, error) {
conn, err := net.Dial(network, address)
if err != nil {
return nil, nil, fmt.Errorf("error connecting to server: %w", err)
}
if err = conn.SetDeadline(time.Now().Add(deps.IOTimeout)); err != nil { // coverage-ignore // hard to simulate this
return nil, nil, fmt.Errorf("failed to set deadline: %w", err)
}
session := networking.NewSession(conn.LocalAddr().String(), conn)
session := services.NewSessionIO(conn.LocalAddr().String(), conn)
return session, conn.Close, nil
})
}
19 changes: 10 additions & 9 deletions cmd/client/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import (
"errors"
"fmt"
"log/slog"
"word-of-wisdom-go/config"
"word-of-wisdom-go/pkg/app/challenges"
"word-of-wisdom-go/pkg/di"
"word-of-wisdom-go/pkg/diag"
"word-of-wisdom-go/pkg/services"
"word-of-wisdom-go/internal/app"
"word-of-wisdom-go/internal/config"
"word-of-wisdom-go/internal/di"
"word-of-wisdom-go/internal/diag"
"word-of-wisdom-go/internal/services"

"github.com/spf13/cobra"
"go.uber.org/dig"
Expand Down Expand Up @@ -71,14 +71,15 @@ func newRootCmd(container *dig.Container) *cobra.Command {
di.ProvideAll(container,
di.ProvideValue(rootLogger),

// app layer
challenges.NewChallenges,

// client deps
// client specific deps
newSessionDialer,
newWOWCommand,
di.ProvideAs[*app.Challenges, challengesService],
),

// app layer
app.Register(container),

// service layer
services.Register(container),
)
Expand Down
3 changes: 1 addition & 2 deletions cmd/client/solve_challenge.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"log/slog"
"os"
"time"
"word-of-wisdom-go/pkg/app/challenges"

"github.com/samber/lo"
"github.com/spf13/cobra"
Expand All @@ -19,7 +18,7 @@ type runSolveChallengeCommandParams struct {

RootLogger *slog.Logger

challenges.Challenges
Challenges challengesService

// client params
challengeToSolve string
Expand Down
30 changes: 17 additions & 13 deletions cmd/client/wow.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,43 @@ import (
"strconv"
"strings"
"time"
"word-of-wisdom-go/pkg/app/challenges"
"word-of-wisdom-go/pkg/services/networking"
"word-of-wisdom-go/internal/api/tcp/commands"
"word-of-wisdom-go/internal/services"

"go.uber.org/dig"
)

type WOWCommand interface {
Process(ctx context.Context, session networking.Session) (string, error)
Process(ctx context.Context, session *services.SessionIO) (string, error)
}

type WOWCommandFunc func(ctx context.Context, session networking.Session) (string, error)
type WOWCommandFunc func(ctx context.Context, session *services.SessionIO) (string, error)

func (f WOWCommandFunc) Process(ctx context.Context, session networking.Session) (string, error) {
func (f WOWCommandFunc) Process(ctx context.Context, session *services.SessionIO) (string, error) {
return f(ctx, session)
}

var _ WOWCommandFunc = WOWCommandFunc(nil)

type challengesService interface {
SolveChallenge(ctx context.Context, complexity int, challenge string) (string, error)
}

type WOWCommandDeps struct {
dig.In

RootLogger *slog.Logger

// app layer
challenges.Challenges
Challenges challengesService
}

func newWOWCommand(deps WOWCommandDeps) WOWCommand {
logger := deps.RootLogger.WithGroup("client")
return WOWCommandFunc(func(ctx context.Context, session networking.Session) (string, error) {
return WOWCommandFunc(func(ctx context.Context, session *services.SessionIO) (string, error) {
logger.DebugContext(ctx, "Sending GET_WOW request")

if err := session.WriteLine("GET_WOW"); err != nil {
if err := session.WriteLine(commands.CommandGetWow); err != nil {
return "", fmt.Errorf("failed to write to the server: %w", err)
}

Expand All @@ -50,18 +54,18 @@ func newWOWCommand(deps WOWCommandDeps) WOWCommand {

logger.DebugContext(ctx, "Got response", slog.String("data", line))

if strings.Index(line, "WOW:") == 0 {
if strings.Index(line, commands.WowResponsePrefix) == 0 {
logger.DebugContext(ctx, "Got WOW response. No challenge required")
return strings.Trim(line[4:], " "), nil
}

if strings.Index(line, "CHALLENGE_REQUIRED:") != 0 {
if strings.Index(line, commands.ChallengeRequiredPrefix) != 0 {
return "", fmt.Errorf("got unexpected challenge requirement response %s: %w", line, err)
}

separatorIndex := strings.Index(line, ";")

challenge := strings.Trim(line[len("CHALLENGE_REQUIRED:"):separatorIndex], " ")
challenge := strings.Trim(line[len(commands.ChallengeRequiredPrefix):separatorIndex], " ")
complexity, err := strconv.Atoi(line[separatorIndex+1:])
if err != nil {
return "", err
Expand All @@ -79,7 +83,7 @@ func newWOWCommand(deps WOWCommandDeps) WOWCommand {
slog.Duration("solutionDuration", time.Since(solveStartedAt)),
slog.String("solution", solution),
)
if err = session.WriteLine("CHALLENGE_RESULT: " + solution); err != nil {
if err = session.WriteLine(commands.ChallengeResultPrefix + solution); err != nil {
return "", fmt.Errorf("failed to write to the server: %w", err)
}

Expand All @@ -90,7 +94,7 @@ func newWOWCommand(deps WOWCommandDeps) WOWCommand {

logger.DebugContext(ctx, "Got response", slog.String("data", line))

if strings.Index(line, "WOW:") == 0 {
if strings.Index(line, commands.WowResponsePrefix) == 0 {
return strings.Trim(line[4:], " "), nil
}

Expand Down
Loading