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
10 changes: 8 additions & 2 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ jobs:
- name: Run server unit tests with coverage
run: |
mkdir ./tests/coverage-ci
go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=github.com/roadrunner-server/server/v6 -coverprofile=./tests/coverage-ci/server_u.out -covermode=atomic ./
go test -timeout 20m -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./tests/coverage-ci/server_u.out -covermode=atomic ./...

- name: Run server e2e tests with coverage
run: |
cd tests
go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=github.com/roadrunner-server/server/v6 -coverprofile=./coverage-ci/server.out -covermode=atomic ./
go test -timeout 20m -v -race -cover -tags=debug -coverpkg=github.com/roadrunner-server/server/v6/... -coverprofile=./coverage-ci/server.out -covermode=atomic ./...

- name: Archive code coverage results
uses: actions/upload-artifact@v7
Expand Down Expand Up @@ -109,6 +109,12 @@ jobs:
}
' summary.txt > summary.filtered.txt
mv summary.filtered.txt summary.txt
# a profile that maps to no plugin source uploads fine and reports 0%
blocks=$(($(wc -l < summary.txt) - 1))
if [ "$blocks" -lt 10 ]; then
echo "::error::coverage summary holds $blocks blocks, the profile does not map to plugin sources"
exit 1
fi

- name: upload to codecov
uses: codecov/codecov-action@v7 # Docs: <https://github.com/codecov/codecov-action>
Expand Down
99 changes: 99 additions & 0 deletions plugin_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package server

import (
"bytes"
"io"
"log/slog"
"os"
"os/user"
"path/filepath"
"runtime"
"strconv"
"testing"
Expand Down Expand Up @@ -345,3 +348,99 @@ func TestEnv4(t *testing.T) {

t.Fatal("FOO not found")
}

func TestName(t *testing.T) {
require.Equal(t, PluginName, (&Plugin{}).Name())
}

// TestUIDGIDWithoutUser covers the nil guard: with no user configured the
// plugin reports 0 rather than dereferencing the unset ids.
func TestUIDGIDWithoutUser(t *testing.T) {
p := &Plugin{}

require.Equal(t, 0, p.UID())
require.Equal(t, 0, p.GID())
}

func TestUIDGIDWithResolvedUser(t *testing.T) {
p := &Plugin{ids: &ids{uid: 1234, gid: 5678}}

require.Equal(t, 1234, p.UID())
require.Equal(t, 5678, p.GID())
}

func TestConfigInitDefaults(t *testing.T) {
t.Run("command is required", func(t *testing.T) {
require.ErrorContains(t, (&Config{}).InitDefaults(), "command should not be empty")
})

t.Run("relay defaults to pipes", func(t *testing.T) {
cfg := &Config{Command: []string{"php", "worker.php"}}
require.NoError(t, cfg.InitDefaults())
require.Equal(t, "pipes", cfg.Relay)
})

t.Run("relay is left alone when set", func(t *testing.T) {
cfg := &Config{Command: []string{"php", "worker.php"}, Relay: "tcp://127.0.0.1:9999"}
require.NoError(t, cfg.InitDefaults())
require.Equal(t, "tcp://127.0.0.1:9999", cfg.Relay)
})

t.Run("on_init command is required", func(t *testing.T) {
cfg := &Config{Command: []string{"php", "worker.php"}, OnInit: &InitConfig{}}
require.ErrorContains(t, cfg.InitDefaults(), "on_init command should not be empty")
})

t.Run("on_init exec timeout defaults to a minute", func(t *testing.T) {
cfg := &Config{
Command: []string{"php", "worker.php"},
OnInit: &InitConfig{Command: []string{"php", "init.php"}},
}
require.NoError(t, cfg.InitDefaults())
require.Equal(t, time.Minute, cfg.OnInit.ExecTimeout)
})

t.Run("on_init exec timeout is left alone when set", func(t *testing.T) {
cfg := &Config{
Command: []string{"php", "worker.php"},
OnInit: &InitConfig{Command: []string{"php", "init.php"}, ExecTimeout: time.Second * 5},
}
require.NoError(t, cfg.InitDefaults())
require.Equal(t, time.Second*5, cfg.OnInit.ExecTimeout)
})
}

// TestCommandWriteForwardsToLogger covers the io.Writer the on_init command's
// output is piped through.
func TestCommandWriteForwardsToLogger(t *testing.T) {
var buf bytes.Buffer
c := newCommand(slog.New(slog.NewTextHandler(&buf, nil)), &InitConfig{})

n, err := c.Write([]byte("hello from on_init"))

require.NoError(t, err)
require.Equal(t, len("hello from on_init"), n)
require.Contains(t, buf.String(), "hello from on_init")
}

// TestCreateProcessAppliesEnv checks config env is uppercased, expanded and
// appended after the OS environment so it wins.
func TestCreateProcessAppliesEnv(t *testing.T) {
t.Setenv("SERVER_TEST_BASE", "expanded")

c := newCommand(slog.New(slog.NewTextHandler(io.Discard, nil)), &InitConfig{})
cmd := c.createProcess(map[string]string{"lower_key": "${SERVER_TEST_BASE}-value"}, []string{"php", "worker.php"})

require.Equal(t, "php", filepath.Base(cmd.Path))
require.Equal(t, []string{"php", "worker.php"}, cmd.Args)
require.Contains(t, cmd.Env, "LOWER_KEY=expanded-value")
}

// TestCreateProcessSingleArgument covers the branch where the command carries
// no arguments.
func TestCreateProcessSingleArgument(t *testing.T) {
c := newCommand(slog.New(slog.NewTextHandler(io.Discard, nil)), &InitConfig{})
cmd := c.createProcess(nil, []string{"php"})

require.Equal(t, []string{"php"}, cmd.Args)
}
7 changes: 4 additions & 3 deletions tests/configs/.rr-env.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
version: '3'

server:
command: "php php_test_files/client.php echo pipes"
command: "php php_test_files/client.php env pipes"
relay: "pipes"
relay_timeout: "20s"
env:
- DATABASE_URL: "mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?serverVersion=5.7"
- RR_PLAIN: "plain-value"
- RR_EXPANDED: "prefix-${RR_TEST_FROM_OS}-suffix"

logs:
mode: development
level: info
level: error
2 changes: 1 addition & 1 deletion tests/configs/.rr-metrics-oninit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ rpc:
listen: tcp://127.0.0.1:6001

server:
command: "php foo"
command: "php php_test_files/client.php echo pipes"
on_init:
command: "php php_test_files/on-init-metrics.php"

12 changes: 0 additions & 12 deletions tests/configs/.rr-no-app-section.yaml

This file was deleted.

47 changes: 47 additions & 0 deletions tests/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package tests

import (
"testing"

"tests/helpers"

"github.com/roadrunner-server/server/v6"
)

// The cases below all describe a broken configuration. Each must be rejected at
// the boot stage where the problem is detectable, rather than starting a server
// that cannot work.

// TestMissingConfigFileFailsInit covers a config path that does not exist.
func TestMissingConfigFileFailsInit(t *testing.T) {
_ = helpers.StartExpectInitError(t, "configs/.rrrrrrrrrr.yaml", []any{&server.Plugin{}},
helpers.WithConfigVersion("v2024.1.0"))
}

// TestUnknownRelayFailsInit covers a relay value the plugin does not implement.
func TestUnknownRelayFailsInit(t *testing.T) {
_ = helpers.StartExpectInitError(t, "configs/.rr-wrong-relay.yaml", []any{&server.Plugin{}},
helpers.WithConfigVersion("v2024.1.0"))
}

// TestUnrunnableCommandFailsServe covers a command that cannot be executed: the
// config is well formed, so this only shows up when workers are allocated.
func TestUnrunnableCommandFailsServe(t *testing.T) {
_ = helpers.StartExpectServeError(t, "configs/.rr-wrong-command.yaml", []any{&server.Plugin{}, &Foo{}},
helpers.WithConfigVersion("v2024.1.0"))
}

// TestUnrunnableOnInitCommandFailsServe is the same for the on_init command.
// The fixture has to be registered as well: the server plugin only reports the
// failure once something asks it for a pool.
func TestUnrunnableOnInitCommandFailsServe(t *testing.T) {
_ = helpers.StartExpectServeError(t, "configs/.rr-wrong-command-on-init.yaml", []any{&server.Plugin{}, &Foo3{}},
helpers.WithConfigVersion("v2024.1.0"))
}

// TestWorkerExceptionFailsServe covers a worker script that throws during
// startup, so the pool cannot be filled.
func TestWorkerExceptionFailsServe(t *testing.T) {
_ = helpers.StartExpectServeError(t, "configs/.rr-script-err.yaml", []any{&server.Plugin{}, &Foo{}},
helpers.WithConfigVersion("v2024.1.0"))
}
32 changes: 1 addition & 31 deletions tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@ module tests

go 1.26

toolchain go1.26.5
toolchain go1.26.6

require (
github.com/roadrunner-server/config/v6 v6.0.0-beta.3
github.com/roadrunner-server/endure/v2 v2.6.2
github.com/roadrunner-server/errors v1.5.0
github.com/roadrunner-server/http/v6 v6.0.0-beta.8
github.com/roadrunner-server/logger/v6 v6.0.0-beta.3
github.com/roadrunner-server/metrics/v6 v6.0.0-beta.5
github.com/roadrunner-server/pool/v2 v2.0.0-beta.1
github.com/roadrunner-server/prometheus/v6 v6.0.0-beta.2
github.com/roadrunner-server/rpc/v6 v6.0.0-beta.5
github.com/roadrunner-server/server/v6 v6.0.0
github.com/stretchr/testify v1.11.1
Expand All @@ -22,38 +20,24 @@ replace github.com/roadrunner-server/server/v6 => ../

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/caddyserver/certmagic v0.25.4 // indirect
github.com/caddyserver/zerossl v0.1.5 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/felixge/httpsnoop v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.10.1 // indirect
github.com/go-logr/logr v1.4.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
github.com/libdns/libdns v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-isatty v0.0.24 // indirect
github.com/mholt/acmez v1.2.0 // indirect
github.com/mholt/acmez/v3 v3.1.6 // indirect
github.com/miekg/dns v1.1.72 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.24.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.70.1 // indirect
github.com/prometheus/procfs v0.21.1 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.61.0 // indirect
github.com/roadrunner-server/api-go/v6 v6.0.0-beta.13 // indirect
github.com/roadrunner-server/api-plugins/v6 v6.0.0-beta.2 // indirect
github.com/roadrunner-server/context v1.3.0 // indirect
github.com/roadrunner-server/events v1.0.1 // indirect
github.com/roadrunner-server/goridge/v4 v4.0.0-beta.3 // indirect
github.com/roadrunner-server/tcplisten v1.5.2 // indirect
Expand All @@ -67,26 +51,12 @@ require (
github.com/tklauser/go-sysconf v0.4.0 // indirect
github.com/tklauser/numcpus v0.12.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zeebo/assert v1.3.1 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.45.0 // indirect
go.opentelemetry.io/otel v1.45.0 // indirect
go.opentelemetry.io/otel/metric v1.45.0 // indirect
go.opentelemetry.io/otel/trace v1.45.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.28.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/crypto v0.55.0 // indirect
golang.org/x/mod v0.39.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.41.0 // indirect
golang.org/x/tools v0.48.0 // indirect
google.golang.org/genproto v0.0.0-20260810153831-ec0a7760b754 // indirect
google.golang.org/protobuf v1.36.12 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading
Loading