mirror of
https://github.com/coder/coder.git
synced 2026-10-16 14:48:56 +08:00
chore: delete dead functions and skip-only or duplicated tests (#29461)
This commit is contained in:
@@ -22,22 +22,3 @@ type ContainerCLI interface {
|
||||
// Remove removes the container
|
||||
Remove(ctx context.Context, containerName string) error
|
||||
}
|
||||
|
||||
// noopContainerCLI is a ContainerCLI that does nothing.
|
||||
type noopContainerCLI struct{}
|
||||
|
||||
var _ ContainerCLI = noopContainerCLI{}
|
||||
|
||||
func (noopContainerCLI) List(_ context.Context) (codersdk.WorkspaceAgentListContainersResponse, error) {
|
||||
return codersdk.WorkspaceAgentListContainersResponse{}, nil
|
||||
}
|
||||
|
||||
func (noopContainerCLI) DetectArchitecture(_ context.Context, _ string) (string, error) {
|
||||
return "<none>", nil
|
||||
}
|
||||
func (noopContainerCLI) Copy(_ context.Context, _ string, _ string, _ string) error { return nil }
|
||||
func (noopContainerCLI) ExecAs(_ context.Context, _ string, _ string, _ ...string) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (noopContainerCLI) Stop(_ context.Context, _ string) error { return nil }
|
||||
func (noopContainerCLI) Remove(_ context.Context, _ string) error { return nil }
|
||||
|
||||
@@ -177,15 +177,6 @@ type devcontainerCLIReadConfigConfig struct {
|
||||
stderr io.Writer
|
||||
}
|
||||
|
||||
// WithReadConfigOutput sets additional stdout and stderr writers for logs
|
||||
// during ReadConfig operations.
|
||||
func WithReadConfigOutput(stdout, stderr io.Writer) DevcontainerCLIReadConfigOptions {
|
||||
return func(o *devcontainerCLIReadConfigConfig) {
|
||||
o.stdout = stdout
|
||||
o.stderr = stderr
|
||||
}
|
||||
}
|
||||
|
||||
func applyDevcontainerCLIUpOptions(opts []DevcontainerCLIUpOptions) DevcontainerCLIUpConfig {
|
||||
conf := DevcontainerCLIUpConfig{Stdout: io.Discard, Stderr: io.Discard}
|
||||
for _, opt := range opts {
|
||||
|
||||
@@ -33,13 +33,6 @@ func WithClock(c quartz.Clock) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithGitBinary overrides the git binary path (for testing).
|
||||
func WithGitBinary(path string) Option {
|
||||
return func(h *Handler) {
|
||||
h.gitBin = path
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// scanCooldown is the minimum interval between successive scans.
|
||||
scanCooldown = 1 * time.Second
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sony/gobreaker/v2"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -187,22 +186,6 @@ func (p *ProviderCircuitBreakers) Execute(endpoint, model string, w http.Respons
|
||||
return handlerErr
|
||||
}
|
||||
|
||||
// Timeout returns the configured timeout duration for this circuit breaker.
|
||||
func (p *ProviderCircuitBreakers) Timeout() time.Duration {
|
||||
return p.config.Timeout
|
||||
}
|
||||
|
||||
// Provider returns the provider name for this circuit breaker.
|
||||
func (p *ProviderCircuitBreakers) Provider() string {
|
||||
return p.provider
|
||||
}
|
||||
|
||||
// OpenErrorResponse returns the error response body when the circuit is open.
|
||||
// This is exposed for handlers to use when responding to rejected requests.
|
||||
func (p *ProviderCircuitBreakers) OpenErrorResponse() []byte {
|
||||
return p.openErrBody()
|
||||
}
|
||||
|
||||
// StateToGaugeValue converts gobreaker.State to a gauge value.
|
||||
// closed=0, half-open=0.5, open=1
|
||||
func StateToGaugeValue(s gobreaker.State) float64 {
|
||||
|
||||
@@ -29,40 +29,6 @@ func newTestAnthropic(t testing.TB, cfg config.Anthropic, bedrockCfg *config.AWS
|
||||
return p
|
||||
}
|
||||
|
||||
func TestAnthropic_TypeAndName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg config.Anthropic
|
||||
expectType string
|
||||
expectName string
|
||||
}{
|
||||
{
|
||||
name: "defaults",
|
||||
cfg: config.Anthropic{},
|
||||
expectType: config.ProviderAnthropic,
|
||||
expectName: config.ProviderAnthropic,
|
||||
},
|
||||
{
|
||||
name: "custom_name",
|
||||
cfg: config.Anthropic{Name: "anthropic-custom"},
|
||||
expectType: config.ProviderAnthropic,
|
||||
expectName: "anthropic-custom",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := newTestAnthropic(t, tc.cfg, nil)
|
||||
assert.Equal(t, tc.expectType, p.Type())
|
||||
assert.Equal(t, tc.expectName, p.Name())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAnthropic_KeyResolution(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -482,30 +482,6 @@ func TestBedrock_CircuitBreakerOpenErrorResponse(t *testing.T) {
|
||||
assert.Equal(t, "service_unavailable", openAIEnvelope.Error.Code)
|
||||
}
|
||||
|
||||
func TestBedrock_TypeAndName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := newTestBedrock(t, config.Anthropic{}, config.AWSBedrock{
|
||||
Region: "us-west-2",
|
||||
AccessKey: "test-key",
|
||||
AccessKeySecret: "test-secret",
|
||||
Model: "m",
|
||||
SmallFastModel: "s",
|
||||
})
|
||||
assert.Equal(t, config.ProviderBedrock, p.Type())
|
||||
assert.Equal(t, config.ProviderBedrock, p.Name())
|
||||
|
||||
p2 := newTestBedrock(t, config.Anthropic{Name: "bedrock-custom"}, config.AWSBedrock{
|
||||
Region: "us-west-2",
|
||||
AccessKey: "test-key",
|
||||
AccessKeySecret: "test-secret",
|
||||
Model: "m",
|
||||
SmallFastModel: "s",
|
||||
})
|
||||
assert.Equal(t, config.ProviderBedrock, p2.Type())
|
||||
assert.Equal(t, "bedrock-custom", p2.Name())
|
||||
}
|
||||
|
||||
func TestBedrock_KeyPool(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -18,40 +18,6 @@ import (
|
||||
|
||||
var testTracer = otel.Tracer("copilot_test")
|
||||
|
||||
func TestCopilot_TypeAndName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg config.Copilot
|
||||
expectType string
|
||||
expectName string
|
||||
}{
|
||||
{
|
||||
name: "defaults",
|
||||
cfg: config.Copilot{},
|
||||
expectType: config.ProviderCopilot,
|
||||
expectName: config.ProviderCopilot,
|
||||
},
|
||||
{
|
||||
name: "custom_name",
|
||||
cfg: config.Copilot{Name: "copilot-business"},
|
||||
expectType: config.ProviderCopilot,
|
||||
expectName: "copilot-business",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := NewCopilot(tc.cfg)
|
||||
assert.Equal(t, tc.expectType, p.Type())
|
||||
assert.Equal(t, tc.expectName, p.Name())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestCopilot_KeyFailoverConfig verifies that Copilot, being BYOK-only,
|
||||
// returns a zero-value KeyFailoverConfig so that KeyFailoverTransport
|
||||
// short-circuits and passes the request through unchanged.
|
||||
|
||||
@@ -164,40 +164,6 @@ func generateResponsesPayload(payloadSize int, inputCount int, stream bool) []by
|
||||
return bodyBytes
|
||||
}
|
||||
|
||||
func TestOpenAI_TypeAndName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg config.OpenAI
|
||||
expectType string
|
||||
expectName string
|
||||
}{
|
||||
{
|
||||
name: "defaults",
|
||||
cfg: config.OpenAI{},
|
||||
expectType: config.ProviderOpenAI,
|
||||
expectName: config.ProviderOpenAI,
|
||||
},
|
||||
{
|
||||
name: "custom_name",
|
||||
cfg: config.OpenAI{Name: "openai-custom"},
|
||||
expectType: config.ProviderOpenAI,
|
||||
expectName: "openai-custom",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := NewOpenAI(tc.cfg)
|
||||
assert.Equal(t, tc.expectType, p.Type())
|
||||
assert.Equal(t, tc.expectName, p.Name())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenAI_CreateInterceptor_Credential(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/coder/coder/v2/aibridge/config"
|
||||
)
|
||||
|
||||
func TestProvider_TypeAndName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
bedrockCfg := config.AWSBedrock{
|
||||
Region: "us-west-2",
|
||||
AccessKey: "test-key",
|
||||
AccessKeySecret: "test-secret",
|
||||
Model: "m",
|
||||
SmallFastModel: "s",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
provider func(t testing.TB) Provider
|
||||
expectType string
|
||||
expectName string
|
||||
}{
|
||||
{
|
||||
name: "anthropic_defaults",
|
||||
provider: func(t testing.TB) Provider { return newTestAnthropic(t, config.Anthropic{}, nil) },
|
||||
expectType: config.ProviderAnthropic,
|
||||
expectName: config.ProviderAnthropic,
|
||||
},
|
||||
{
|
||||
name: "anthropic_custom_name",
|
||||
provider: func(t testing.TB) Provider {
|
||||
return newTestAnthropic(t, config.Anthropic{Name: "anthropic-custom"}, nil)
|
||||
},
|
||||
expectType: config.ProviderAnthropic,
|
||||
expectName: "anthropic-custom",
|
||||
},
|
||||
{
|
||||
name: "bedrock_defaults",
|
||||
provider: func(t testing.TB) Provider { return newTestBedrock(t, config.Anthropic{}, bedrockCfg) },
|
||||
expectType: config.ProviderBedrock,
|
||||
expectName: config.ProviderBedrock,
|
||||
},
|
||||
{
|
||||
name: "bedrock_custom_name",
|
||||
provider: func(t testing.TB) Provider {
|
||||
return newTestBedrock(t, config.Anthropic{Name: "bedrock-custom"}, bedrockCfg)
|
||||
},
|
||||
expectType: config.ProviderBedrock,
|
||||
expectName: "bedrock-custom",
|
||||
},
|
||||
{
|
||||
name: "copilot_defaults",
|
||||
provider: func(testing.TB) Provider { return NewCopilot(config.Copilot{}) },
|
||||
expectType: config.ProviderCopilot,
|
||||
expectName: config.ProviderCopilot,
|
||||
},
|
||||
{
|
||||
name: "copilot_custom_name",
|
||||
provider: func(testing.TB) Provider { return NewCopilot(config.Copilot{Name: "copilot-business"}) },
|
||||
expectType: config.ProviderCopilot,
|
||||
expectName: "copilot-business",
|
||||
},
|
||||
{
|
||||
name: "openai_defaults",
|
||||
provider: func(testing.TB) Provider { return NewOpenAI(config.OpenAI{}) },
|
||||
expectType: config.ProviderOpenAI,
|
||||
expectName: config.ProviderOpenAI,
|
||||
},
|
||||
{
|
||||
name: "openai_custom_name",
|
||||
provider: func(testing.TB) Provider { return NewOpenAI(config.OpenAI{Name: "openai-custom"}) },
|
||||
expectType: config.ProviderOpenAI,
|
||||
expectName: "openai-custom",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := tc.provider(t)
|
||||
assert.Equal(t, tc.expectType, p.Type())
|
||||
assert.Equal(t, tc.expectName, p.Name())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -77,12 +77,3 @@ func InterceptionAttributesFromContext(ctx context.Context) []attribute.KeyValue
|
||||
func WithRequestBridgeAttributesInContext(ctx context.Context, traceAttrs []attribute.KeyValue) context.Context {
|
||||
return context.WithValue(ctx, traceRequestBridgeAttrsContextKey{}, traceAttrs)
|
||||
}
|
||||
|
||||
func RequestBridgeAttributesFromContext(ctx context.Context) []attribute.KeyValue {
|
||||
attrs, ok := ctx.Value(traceRequestBridgeAttrsContextKey{}).([]attribute.KeyValue)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
@@ -67,12 +67,6 @@ func WithStackdriver(loc string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithTrace() Option {
|
||||
return func(b *Builder) {
|
||||
b.Trace = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithVerbose() Option {
|
||||
return func(b *Builder) {
|
||||
b.Verbose = true
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/provisioner/echo"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
"github.com/coder/quartz"
|
||||
"github.com/coder/serpent"
|
||||
)
|
||||
|
||||
@@ -41,18 +40,6 @@ func New(t testing.TB, args ...string) (*serpent.Invocation, config.Root) {
|
||||
return NewWithCommand(t, cmd, args...)
|
||||
}
|
||||
|
||||
// NewWithClock is like New, but injects the given clock for
|
||||
// tests that are time-dependent.
|
||||
func NewWithClock(t testing.TB, clk quartz.Clock, args ...string) (*serpent.Invocation, config.Root) {
|
||||
var root cli.RootCmd
|
||||
root.SetClock(clk)
|
||||
|
||||
cmd, err := root.Command(root.AGPL())
|
||||
require.NoError(t, err)
|
||||
|
||||
return NewWithCommand(t, cmd, args...)
|
||||
}
|
||||
|
||||
type logWriter struct {
|
||||
prefix string
|
||||
log slog.Logger
|
||||
|
||||
@@ -151,11 +151,6 @@ func Capture(inv *serpent.Invocation) *Output {
|
||||
return output
|
||||
}
|
||||
|
||||
// Golden returns the formatted output with lines prefixed by "err: " or "out: ".
|
||||
func (o *Output) Golden() []byte {
|
||||
return o.combined.Bytes()
|
||||
}
|
||||
|
||||
// Stdout returns the unprefixed stdout content for parsing (e.g., JSON).
|
||||
func (o *Output) Stdout() string {
|
||||
return o.stdout.String()
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
// Queue is a FIFO queue with a fixed size. If the size is exceeded, the first
|
||||
@@ -89,72 +87,3 @@ func (q *Queue[T]) Len() int {
|
||||
defer q.mu.Unlock()
|
||||
return len(q.items)
|
||||
}
|
||||
|
||||
type reportTask struct {
|
||||
link string
|
||||
messageID int64
|
||||
selfReported bool
|
||||
state codersdk.WorkspaceAppStatusState
|
||||
summary string
|
||||
}
|
||||
|
||||
// statusQueue is a Queue that:
|
||||
// 1. Only pushes items that are not duplicates.
|
||||
// 2. Preserves the existing message and URI when one a message is not provided.
|
||||
// 3. Ignores "working" updates from the status watcher.
|
||||
type StatusQueue struct {
|
||||
Queue[reportTask]
|
||||
// lastMessageID is the ID of the last *user* message that we saw. A user
|
||||
// message only happens when interacting via the API (as opposed to
|
||||
// interacting with the terminal directly).
|
||||
lastMessageID int64
|
||||
}
|
||||
|
||||
func (q *StatusQueue) Push(report reportTask) error {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
if q.closed {
|
||||
return xerrors.New("queue has been closed")
|
||||
}
|
||||
var lastReport reportTask
|
||||
if len(q.items) > 0 {
|
||||
lastReport = q.items[len(q.items)-1]
|
||||
}
|
||||
// Use "working" status if this is a new user message. If this is not a new
|
||||
// user message, and the status is "working" and not self-reported (meaning it
|
||||
// came from the screen watcher), then it means one of two things:
|
||||
// 1. The LLM is still working, in which case our last status will already
|
||||
// have been "working", so there is nothing to do.
|
||||
// 2. The user has interacted with the terminal directly. For now, we are
|
||||
// ignoring these updates. This risks missing cases where the user
|
||||
// manually submits a new prompt and the LLM becomes active and does not
|
||||
// update itself, but it avoids spamming useless status updates as the user
|
||||
// is typing, so the tradeoff is worth it. In the future, if we can
|
||||
// reliably distinguish between user and LLM activity, we can change this.
|
||||
if report.messageID > q.lastMessageID {
|
||||
report.state = codersdk.WorkspaceAppStatusStateWorking
|
||||
} else if report.state == codersdk.WorkspaceAppStatusStateWorking && !report.selfReported {
|
||||
q.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
// Preserve previous message and URI if there was no message.
|
||||
if report.summary == "" {
|
||||
report.summary = lastReport.summary
|
||||
if report.link == "" {
|
||||
report.link = lastReport.link
|
||||
}
|
||||
}
|
||||
// Avoid queueing duplicate updates.
|
||||
if report.state == lastReport.state &&
|
||||
report.link == lastReport.link &&
|
||||
report.summary == lastReport.summary {
|
||||
return nil
|
||||
}
|
||||
// Drop the first item if the queue has gotten too big.
|
||||
if len(q.items) >= q.size {
|
||||
q.items = q.items[1:]
|
||||
}
|
||||
q.items = append(q.items, report)
|
||||
q.cond.Broadcast()
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-7
@@ -602,7 +602,7 @@ type RootCmd struct {
|
||||
useKeyringWithGlobalConfig bool
|
||||
|
||||
// clock is used for time-dependent operations. Initialized to
|
||||
// quartz.NewReal() in Command() if not set via SetClock.
|
||||
// quartz.NewReal() in Command() if unset.
|
||||
clock quartz.Clock
|
||||
|
||||
// TLS configuration for custom CA or client certificates.
|
||||
@@ -612,12 +612,6 @@ type RootCmd struct {
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
// SetClock sets the clock used for time-dependent operations.
|
||||
// Must be called before Command() to take effect.
|
||||
func (r *RootCmd) SetClock(clk quartz.Clock) {
|
||||
r.clock = clk
|
||||
}
|
||||
|
||||
// ensureClientURL loads the client URL from the config file if it
|
||||
// wasn't provided via --url or CODER_URL.
|
||||
func (r *RootCmd) ensureClientURL() error {
|
||||
|
||||
@@ -2646,12 +2646,6 @@ func isReplicaRelayRequest(r *http.Request) bool {
|
||||
return r.Header.Get("X-Coder-Relay-Source-Replica") != ""
|
||||
}
|
||||
|
||||
// IsLocalhost returns true if the host points to the local machine. Intended to
|
||||
// be called with `u.Hostname()`.
|
||||
func IsLocalhost(host string) bool {
|
||||
return host == "localhost" || host == "127.0.0.1" || host == "::1"
|
||||
}
|
||||
|
||||
// PostgresConnectOptions contains options for connecting to Postgres.
|
||||
type PostgresConnectOptions struct {
|
||||
MaxOpenConns int
|
||||
|
||||
+62
-118
@@ -1750,62 +1750,31 @@ func TestServer(t *testing.T) {
|
||||
t.Run("Logging", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("CreatesFile", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
fiName := testutil.TempFile(t, "", "coder-logging-test-*")
|
||||
sinks := []struct {
|
||||
flag string
|
||||
file string
|
||||
}{
|
||||
{flag: "--log-human", file: testutil.TempFile(t, "", "coder-logging-test-*")},
|
||||
{flag: "--log-json", file: testutil.TempFile(t, "", "coder-logging-test-*")},
|
||||
}
|
||||
args := []string{
|
||||
"server",
|
||||
"--log-filter=.*",
|
||||
dbArg(t),
|
||||
"--http-address", "127.0.0.1:0",
|
||||
"--access-url", "http://example.com",
|
||||
"--provisioner-daemons=3",
|
||||
"--provisioner-types=echo",
|
||||
}
|
||||
for _, sink := range sinks {
|
||||
args = append(args, sink.flag, sink.file)
|
||||
}
|
||||
root, _ := clitest.New(t, args...)
|
||||
startIgnoringPostgresQueryCancel(t, root)
|
||||
|
||||
root, _ := clitest.New(t,
|
||||
"server",
|
||||
"--log-filter=.*",
|
||||
dbArg(t),
|
||||
"--http-address", "127.0.0.1:0",
|
||||
"--access-url", "http://example.com",
|
||||
"--provisioner-daemons=3",
|
||||
"--provisioner-types=echo",
|
||||
"--log-human", fiName,
|
||||
)
|
||||
startIgnoringPostgresQueryCancel(t, root)
|
||||
|
||||
loggingWaitFile(t, fiName, testutil.WaitLong)
|
||||
})
|
||||
|
||||
t.Run("Human", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
fi := testutil.TempFile(t, "", "coder-logging-test-*")
|
||||
|
||||
root, _ := clitest.New(t,
|
||||
"server",
|
||||
"--log-filter=.*",
|
||||
dbArg(t),
|
||||
"--http-address", "127.0.0.1:0",
|
||||
"--access-url", "http://example.com",
|
||||
"--provisioner-daemons=3",
|
||||
"--provisioner-types=echo",
|
||||
"--log-human", fi,
|
||||
)
|
||||
startIgnoringPostgresQueryCancel(t, root)
|
||||
|
||||
loggingWaitFile(t, fi, testutil.WaitShort)
|
||||
})
|
||||
|
||||
t.Run("JSON", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
fi := testutil.TempFile(t, "", "coder-logging-test-*")
|
||||
|
||||
root, _ := clitest.New(t,
|
||||
"server",
|
||||
"--log-filter=.*",
|
||||
dbArg(t),
|
||||
"--http-address", "127.0.0.1:0",
|
||||
"--access-url", "http://example.com",
|
||||
"--provisioner-daemons=3",
|
||||
"--provisioner-types=echo",
|
||||
"--log-json", fi,
|
||||
)
|
||||
startIgnoringPostgresQueryCancel(t, root)
|
||||
|
||||
loggingWaitFile(t, fi, testutil.WaitShort)
|
||||
})
|
||||
for _, sink := range sinks {
|
||||
loggingWaitFile(t, sink.file, testutil.WaitLong)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("YAML", func(t *testing.T) {
|
||||
@@ -2110,7 +2079,7 @@ func TestServer_ExternalAuthGitHubDefaultProvider(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:tparallel,paralleltest // This test sets environment variables.
|
||||
//nolint:paralleltest // This test sets environment variables.
|
||||
func TestServer_Logging_NoParallel(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = io.Copy(io.Discard, r.Body)
|
||||
@@ -2134,73 +2103,48 @@ func TestServer_Logging_NoParallel(t *testing.T) {
|
||||
// I know; it was made up for the Go package.
|
||||
t.Setenv("GCE_METADATA_HOST", server.URL)
|
||||
|
||||
t.Run("Stackdriver", func(t *testing.T) {
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), testutil.WaitSuperLong)
|
||||
defer cancelFunc()
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), testutil.WaitSuperLong)
|
||||
defer cancelFunc()
|
||||
|
||||
fi := testutil.TempFile(t, "", "coder-logging-test-*")
|
||||
sinks := []struct {
|
||||
flag string
|
||||
file string
|
||||
}{
|
||||
{flag: "--log-human", file: testutil.TempFile(t, "", "coder-logging-test-*")},
|
||||
{flag: "--log-json", file: testutil.TempFile(t, "", "coder-logging-test-*")},
|
||||
{flag: "--log-stackdriver", file: testutil.TempFile(t, "", "coder-logging-test-*")},
|
||||
}
|
||||
args := []string{
|
||||
"server",
|
||||
"--log-filter=.*",
|
||||
dbArg(t),
|
||||
"--http-address", "127.0.0.1:0",
|
||||
"--access-url", "http://example.com",
|
||||
"--provisioner-daemons=3",
|
||||
"--provisioner-types=echo",
|
||||
}
|
||||
for _, sink := range sinks {
|
||||
args = append(args, sink.flag, sink.file)
|
||||
}
|
||||
|
||||
inv, _ := clitest.New(t,
|
||||
"server",
|
||||
"--log-filter=.*",
|
||||
dbArg(t),
|
||||
"--http-address", "127.0.0.1:0",
|
||||
"--access-url", "http://example.com",
|
||||
"--provisioner-daemons=3",
|
||||
"--provisioner-types=echo",
|
||||
"--log-stackdriver", fi,
|
||||
)
|
||||
// Attach expecter so we get debug output from the command if this test
|
||||
// fails.
|
||||
stdout := expecter.NewAttachedToInvocation(t, inv)
|
||||
// NOTE(mafredri): This test might end up downloading Terraform
|
||||
// which can take a long time and end up failing the test.
|
||||
// This is why we wait extra long below for server to listen on
|
||||
// HTTP.
|
||||
inv, _ := clitest.New(t, args...)
|
||||
// Attach expecter so we get debug output from the command if this test
|
||||
// fails.
|
||||
stdout := expecter.NewAttachedToInvocation(t, inv)
|
||||
|
||||
startIgnoringPostgresQueryCancel(t, inv.WithContext(ctx))
|
||||
startIgnoringPostgresQueryCancel(t, inv.WithContext(ctx))
|
||||
|
||||
// Wait for server to listen on HTTP, this is a good
|
||||
// starting point for expecting logs.
|
||||
_ = stdout.ExpectMatch(ctx, "Started HTTP listener at")
|
||||
// Wait for server to listen on HTTP, this is a good
|
||||
// starting point for expecting logs.
|
||||
_ = stdout.ExpectMatch(ctx, "Started HTTP listener at")
|
||||
|
||||
loggingWaitFile(t, fi, testutil.WaitSuperLong)
|
||||
})
|
||||
|
||||
t.Run("Multiple", func(t *testing.T) {
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), testutil.WaitSuperLong)
|
||||
defer cancelFunc()
|
||||
|
||||
fi1 := testutil.TempFile(t, "", "coder-logging-test-*")
|
||||
fi2 := testutil.TempFile(t, "", "coder-logging-test-*")
|
||||
fi3 := testutil.TempFile(t, "", "coder-logging-test-*")
|
||||
|
||||
// NOTE(mafredri): This test might end up downloading Terraform
|
||||
// which can take a long time and end up failing the test.
|
||||
// This is why we wait extra long below for server to listen on
|
||||
// HTTP.
|
||||
inv, _ := clitest.New(t,
|
||||
"server",
|
||||
"--log-filter=.*",
|
||||
dbArg(t),
|
||||
"--http-address", "127.0.0.1:0",
|
||||
"--access-url", "http://example.com",
|
||||
"--provisioner-daemons=3",
|
||||
"--provisioner-types=echo",
|
||||
"--log-human", fi1,
|
||||
"--log-json", fi2,
|
||||
"--log-stackdriver", fi3,
|
||||
)
|
||||
// Attach expecter so we get debug output from the command if this test
|
||||
// fails.
|
||||
stdout := expecter.NewAttachedToInvocation(t, inv)
|
||||
|
||||
startIgnoringPostgresQueryCancel(t, inv)
|
||||
|
||||
// Wait for server to listen on HTTP, this is a good
|
||||
// starting point for expecting logs.
|
||||
_ = stdout.ExpectMatch(ctx, "Started HTTP listener at")
|
||||
|
||||
loggingWaitFile(t, fi1, testutil.WaitSuperLong)
|
||||
loggingWaitFile(t, fi2, testutil.WaitSuperLong)
|
||||
loggingWaitFile(t, fi3, testutil.WaitSuperLong)
|
||||
})
|
||||
for _, sink := range sinks {
|
||||
loggingWaitFile(t, sink.file, testutil.WaitSuperLong)
|
||||
}
|
||||
}
|
||||
|
||||
func loggingWaitFile(t *testing.T, fiName string, dur time.Duration) {
|
||||
|
||||
@@ -97,17 +97,6 @@ func (a RBACAsserter) AssertChecked(t *testing.T, action policy.Action, objects
|
||||
a.Recorder.AssertOutOfOrder(t, a.Subject, pairs...)
|
||||
}
|
||||
|
||||
// AssertInOrder must be called in the correct order of authz checks. If the objects
|
||||
// or actions are not in the correct order, the test will fail.
|
||||
func (a RBACAsserter) AssertInOrder(t *testing.T, action policy.Action, objects ...interface{}) {
|
||||
converted := a.convertObjects(t, objects...)
|
||||
pairs := make([]ActionObjectPair, 0, len(converted))
|
||||
for _, obj := range converted {
|
||||
pairs = append(pairs, a.Recorder.Pair(action, obj))
|
||||
}
|
||||
a.Recorder.AssertActor(t, a.Subject, pairs...)
|
||||
}
|
||||
|
||||
// convertObjects converts the codersdk types to rbac.Object. Unfortunately
|
||||
// does not have type safety, and instead uses a t.Fatal to enforce types.
|
||||
func (RBACAsserter) convertObjects(t *testing.T, objs ...interface{}) []rbac.Object {
|
||||
|
||||
@@ -1417,11 +1417,6 @@ func AgentsReady(agent codersdk.WorkspaceAgent) bool {
|
||||
return agent.LifecycleState == codersdk.WorkspaceAgentLifecycleReady
|
||||
}
|
||||
|
||||
// AgentsNotReady checks that the latest lifecycle state of an agent is anything except "Ready".
|
||||
func AgentsNotReady(agent codersdk.WorkspaceAgent) bool {
|
||||
return !AgentsReady(agent)
|
||||
}
|
||||
|
||||
// WaitFor waits for the given criteria and fails the test if they are not met before the
|
||||
// waiter's context is canceled.
|
||||
func (w WorkspaceAgentWaiter) WaitFor(criteria ...WaitForAgentFn) {
|
||||
|
||||
@@ -250,12 +250,6 @@ func WithPKCE() func(*FakeIDP) {
|
||||
}
|
||||
}
|
||||
|
||||
func WithAuthorizedRedirectURL(hook func(redirectURL string) error) func(*FakeIDP) {
|
||||
return func(f *FakeIDP) {
|
||||
f.hookValidRedirectURL = hook
|
||||
}
|
||||
}
|
||||
|
||||
func WithMiddlewares(mws ...func(http.Handler) http.Handler) func(*FakeIDP) {
|
||||
return func(f *FakeIDP) {
|
||||
f.middlewares = append(f.middlewares, mws...)
|
||||
|
||||
@@ -77,12 +77,6 @@ func WithClock(clock quartz.Clock) RotatorOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithKeyDuration(keyDuration time.Duration) RotatorOption {
|
||||
return func(r *rotator) {
|
||||
r.keyDuration = keyDuration
|
||||
}
|
||||
}
|
||||
|
||||
// WithFeatures sets the crypto key features the rotator manages, replacing the
|
||||
// default set. Use this to opt experiment- or deployment-gated features (such
|
||||
// as the NATS cluster CA) into rotation only when their owner is active.
|
||||
|
||||
@@ -354,12 +354,6 @@ func User(user database.User, organizationIDs []uuid.UUID) codersdk.User {
|
||||
return convertedUser
|
||||
}
|
||||
|
||||
func Users(users []database.User, organizationIDs map[uuid.UUID][]uuid.UUID) []codersdk.User {
|
||||
return slice.List(users, func(user database.User) codersdk.User {
|
||||
return User(user, organizationIDs[user.ID])
|
||||
})
|
||||
}
|
||||
|
||||
func Group(row database.GetGroupsRow, members []database.GroupMember, totalMemberCount int) codersdk.Group {
|
||||
return codersdk.Group{
|
||||
ID: row.Group.ID,
|
||||
|
||||
@@ -52,12 +52,6 @@ func (b OrganizationBuilder) Deleted(deleted bool) OrganizationBuilder {
|
||||
return b
|
||||
}
|
||||
|
||||
func (b OrganizationBuilder) Seed(seed database.Organization) OrganizationBuilder {
|
||||
//nolint: revive // returns modified struct
|
||||
b.seed = seed
|
||||
return b
|
||||
}
|
||||
|
||||
func (b OrganizationBuilder) Members(users ...database.User) OrganizationBuilder {
|
||||
for _, u := range users {
|
||||
//nolint: revive // returns modified struct
|
||||
|
||||
@@ -38,7 +38,6 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/x/chatd/chatprompt"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/cryptorand"
|
||||
"github.com/coder/coder/v2/provisionerd/proto"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
@@ -652,18 +651,6 @@ func APIKey(t testing.TB, db database.Store, seed database.APIKey, munge ...func
|
||||
return key, fmt.Sprintf("%s-%s", key.ID, secret)
|
||||
}
|
||||
|
||||
func WorkspaceAgentPortShare(t testing.TB, db database.Store, orig database.WorkspaceAgentPortShare) database.WorkspaceAgentPortShare {
|
||||
ps, err := db.UpsertWorkspaceAgentPortShare(genCtx, database.UpsertWorkspaceAgentPortShareParams{
|
||||
WorkspaceID: takeFirst(orig.WorkspaceID, uuid.New()),
|
||||
AgentName: takeFirst(orig.AgentName, testutil.GetRandomName(t)),
|
||||
Port: takeFirst(orig.Port, 8080),
|
||||
ShareLevel: takeFirst(orig.ShareLevel, database.AppSharingLevelPublic),
|
||||
Protocol: takeFirst(orig.Protocol, database.PortShareProtocolHttp),
|
||||
})
|
||||
require.NoError(t, err, "insert workspace agent")
|
||||
return ps
|
||||
}
|
||||
|
||||
func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgent) database.WorkspaceAgent {
|
||||
agt, err := db.InsertWorkspaceAgent(genCtx, database.InsertWorkspaceAgentParams{
|
||||
ID: takeFirst(orig.ID, uuid.New()),
|
||||
@@ -830,20 +817,6 @@ func WorkspaceAgentScriptTiming(t testing.TB, db database.Store, orig database.W
|
||||
panic("failed to insert workspace agent script timing")
|
||||
}
|
||||
|
||||
func WorkspaceAgentDevcontainer(t testing.TB, db database.Store, orig database.WorkspaceAgentDevcontainer) database.WorkspaceAgentDevcontainer {
|
||||
devcontainers, err := db.InsertWorkspaceAgentDevcontainers(genCtx, database.InsertWorkspaceAgentDevcontainersParams{
|
||||
WorkspaceAgentID: takeFirst(orig.WorkspaceAgentID, uuid.New()),
|
||||
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
|
||||
ID: []uuid.UUID{takeFirst(orig.ID, uuid.New())},
|
||||
Name: []string{takeFirst(orig.Name, testutil.GetRandomName(t))},
|
||||
WorkspaceFolder: []string{takeFirst(orig.WorkspaceFolder, "/workspace")},
|
||||
ConfigPath: []string{takeFirst(orig.ConfigPath, "")},
|
||||
SubagentID: []uuid.UUID{orig.SubagentID.UUID},
|
||||
})
|
||||
require.NoError(t, err, "insert workspace agent devcontainer")
|
||||
return devcontainers[0]
|
||||
}
|
||||
|
||||
func Workspace(t testing.TB, db database.Store, orig database.WorkspaceTable) database.WorkspaceTable {
|
||||
t.Helper()
|
||||
|
||||
@@ -1446,21 +1419,6 @@ func WorkspaceAppStat(t testing.TB, db database.Store, orig database.WorkspaceAp
|
||||
return scheme
|
||||
}
|
||||
|
||||
func WorkspaceAppStatus(t testing.TB, db database.Store, orig database.WorkspaceAppStatus) database.WorkspaceAppStatus {
|
||||
appStatus, err := db.InsertWorkspaceAppStatus(genCtx, database.InsertWorkspaceAppStatusParams{
|
||||
ID: takeFirst(orig.ID, uuid.New()),
|
||||
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
|
||||
WorkspaceID: takeFirst(orig.WorkspaceID, uuid.New()),
|
||||
AgentID: takeFirst(orig.AgentID, uuid.New()),
|
||||
AppID: takeFirst(orig.AppID, uuid.New()),
|
||||
State: takeFirst(orig.State, database.WorkspaceAppStatusStateWorking),
|
||||
Message: takeFirst(orig.Message, ""),
|
||||
Uri: takeFirst(orig.Uri, sql.NullString{}),
|
||||
})
|
||||
require.NoError(t, err, "insert workspace agent status")
|
||||
return appStatus
|
||||
}
|
||||
|
||||
func WorkspaceResource(t testing.TB, db database.Store, orig database.WorkspaceResource) database.WorkspaceResource {
|
||||
resource, err := db.InsertWorkspaceResource(genCtx, database.InsertWorkspaceResourceParams{
|
||||
ID: takeFirst(orig.ID, uuid.New()),
|
||||
@@ -1645,16 +1603,6 @@ func TemplateVersionVariable(t testing.TB, db database.Store, orig database.Temp
|
||||
return version
|
||||
}
|
||||
|
||||
func TemplateVersionWorkspaceTag(t testing.TB, db database.Store, orig database.TemplateVersionWorkspaceTag) database.TemplateVersionWorkspaceTag {
|
||||
workspaceTag, err := db.InsertTemplateVersionWorkspaceTag(genCtx, database.InsertTemplateVersionWorkspaceTagParams{
|
||||
TemplateVersionID: takeFirst(orig.TemplateVersionID, uuid.New()),
|
||||
Key: takeFirst(orig.Key, testutil.GetRandomName(t)),
|
||||
Value: takeFirst(orig.Value, testutil.GetRandomName(t)),
|
||||
})
|
||||
require.NoError(t, err, "insert template version workspace tag")
|
||||
return workspaceTag
|
||||
}
|
||||
|
||||
func TemplateVersionParameter(t testing.TB, db database.Store, orig database.TemplateVersionParameter) database.TemplateVersionParameter {
|
||||
t.Helper()
|
||||
|
||||
@@ -1682,34 +1630,6 @@ func TemplateVersionParameter(t testing.TB, db database.Store, orig database.Tem
|
||||
return version
|
||||
}
|
||||
|
||||
func TemplateVersionTerraformValues(t testing.TB, db database.Store, orig database.TemplateVersionTerraformValue) database.TemplateVersionTerraformValue {
|
||||
t.Helper()
|
||||
|
||||
jobID := uuid.New()
|
||||
if orig.TemplateVersionID != uuid.Nil {
|
||||
v, err := db.GetTemplateVersionByID(genCtx, orig.TemplateVersionID)
|
||||
if err == nil {
|
||||
jobID = v.JobID
|
||||
}
|
||||
}
|
||||
|
||||
params := database.InsertTemplateVersionTerraformValuesByJobIDParams{
|
||||
JobID: jobID,
|
||||
CachedPlan: takeFirstSlice(orig.CachedPlan, []byte("{}")),
|
||||
CachedModuleFiles: orig.CachedModuleFiles,
|
||||
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
|
||||
ProvisionerdVersion: takeFirst(orig.ProvisionerdVersion, proto.CurrentVersion.String()),
|
||||
}
|
||||
|
||||
err := db.InsertTemplateVersionTerraformValuesByJobID(genCtx, params)
|
||||
require.NoError(t, err, "insert template version parameter")
|
||||
|
||||
v, err := db.GetTemplateVersionTerraformValues(genCtx, orig.TemplateVersionID)
|
||||
require.NoError(t, err, "get template version values")
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// WorkspaceAgentStat inserts a workspace agent stat row. Seed the
|
||||
// session_counts column by setting SessionCounts on orig, for example with
|
||||
// the SessionCounts helper:
|
||||
|
||||
@@ -151,14 +151,6 @@ func WithDBFrom(dbFrom string) OpenOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogDSN sets whether the DSN should be logged during testing.
|
||||
// This provides an ergonomic way to connect to test databases during debugging.
|
||||
func WithLogDSN(logDSN bool) OpenOption {
|
||||
return func(o *OpenOptions) {
|
||||
o.LogDSN = logDSN
|
||||
}
|
||||
}
|
||||
|
||||
// TBSubset is a subset of the testing.TB interface.
|
||||
// It allows to use dbtestutil.Open outside of tests.
|
||||
type TBSubset interface {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
||||
package httpapi
|
||||
|
||||
import "net/http"
|
||||
|
||||
// NoopResponseWriter is a response writer that does nothing.
|
||||
type NoopResponseWriter struct{}
|
||||
|
||||
func (NoopResponseWriter) Header() http.Header { return http.Header{} }
|
||||
func (NoopResponseWriter) Write(p []byte) (int, error) { return len(p), nil }
|
||||
func (NoopResponseWriter) WriteHeader(int) {}
|
||||
@@ -31,11 +31,6 @@ func UserParam(r *http.Request) database.User {
|
||||
return user
|
||||
}
|
||||
|
||||
func UserParamOptional(r *http.Request) (database.User, bool) {
|
||||
user, ok := r.Context().Value(userParamContextKey{}).(database.User)
|
||||
return user, ok
|
||||
}
|
||||
|
||||
// ExtractUserParam extracts a user from an ID/username in the {user} URL
|
||||
// parameter.
|
||||
func ExtractUserParam(db database.Store) func(http.Handler) http.Handler {
|
||||
@@ -53,22 +48,6 @@ func ExtractUserParam(db database.Store) func(http.Handler) http.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// ExtractUserParamOptional does not fail if no user is present.
|
||||
func ExtractUserParamOptional(db database.Store) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
user, ok := ExtractUserContext(ctx, db, &httpapi.NoopResponseWriter{}, r)
|
||||
if ok {
|
||||
ctx = context.WithValue(ctx, userParamContextKey{}, user)
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ExtractUserContext queries the database for the parameterized `{user}` from the request URL.
|
||||
func ExtractUserContext(ctx context.Context, db database.Store, rw http.ResponseWriter, r *http.Request) (user database.User, ok bool) {
|
||||
// userQuery is either a uuid, a username, or 'me'
|
||||
|
||||
@@ -719,14 +719,6 @@ func ConfigWithACL() regosql.ConvertConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigWithoutACL is the basic configuration for converting rego to SQL when
|
||||
// the object has no ACL fields.
|
||||
func ConfigWithoutACL() regosql.ConvertConfig {
|
||||
return regosql.ConvertConfig{
|
||||
VariableConverter: regosql.NoACLConverter(),
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigChats uses a resource converter so SQL filters qualify chat
|
||||
// ACL columns consistently with GetChats.
|
||||
func ConfigChats() regosql.ConvertConfig {
|
||||
|
||||
@@ -12,28 +12,6 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
)
|
||||
|
||||
// NoopResolver implements the Resolver interface
|
||||
var _ Resolver = &NoopResolver{}
|
||||
|
||||
// NoopResolver is a useful test device.
|
||||
type NoopResolver struct{}
|
||||
|
||||
func NewNoopResolver() *NoopResolver {
|
||||
return &NoopResolver{}
|
||||
}
|
||||
|
||||
func (NoopResolver) GetRuntimeConfig(context.Context, string) (string, error) {
|
||||
return "", ErrEntryNotFound
|
||||
}
|
||||
|
||||
func (NoopResolver) UpsertRuntimeConfig(context.Context, string, string) error {
|
||||
return ErrEntryNotFound
|
||||
}
|
||||
|
||||
func (NoopResolver) DeleteRuntimeConfig(context.Context, string) error {
|
||||
return ErrEntryNotFound
|
||||
}
|
||||
|
||||
// StoreResolver implements the Resolver interface
|
||||
var _ Resolver = &StoreResolver{}
|
||||
|
||||
|
||||
@@ -1027,12 +1027,6 @@ func (r *remoteReporter) collectUserSecretsSummary(ctx context.Context) (*UserSe
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HashContent returns a SHA256 hash of the content as a hex string.
|
||||
// This is useful for hashing sensitive content like prompts for telemetry.
|
||||
func HashContent(content string) string {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(content)))
|
||||
}
|
||||
|
||||
// ConvertAPIKey anonymizes an API key.
|
||||
func ConvertAPIKey(apiKey database.APIKey) APIKey {
|
||||
a := APIKey{
|
||||
|
||||
@@ -202,12 +202,6 @@ func EndHTTPSpan(r *http.Request, status int, span trace.Span, serverName string
|
||||
|
||||
type tracerNameKey struct{}
|
||||
|
||||
// SetTracerName sets the tracer name that will be used by all spans created
|
||||
// from the context.
|
||||
func SetTracerName(ctx context.Context, tracerName string) context.Context {
|
||||
return context.WithValue(ctx, tracerNameKey{}, tracerName)
|
||||
}
|
||||
|
||||
// GetTracerName returns the tracer name from the context, or TracerName if none
|
||||
// is set.
|
||||
func GetTracerName(ctx context.Context) string {
|
||||
|
||||
@@ -3048,54 +3048,6 @@ func TestDormantUser(t *testing.T) {
|
||||
require.Equal(t, codersdk.UserStatusActive, users.Users[0].Status)
|
||||
}
|
||||
|
||||
// TestSuspendedPagination is when the after_id is a suspended record.
|
||||
// The database query should still return the correct page, as the after_id
|
||||
// is in a subquery that finds the record regardless of its status.
|
||||
// This is mainly to confirm the db fake has the same behavior.
|
||||
func TestSuspendedPagination(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Skip("This fails when two users are created at the exact same time. The reason is unknown... See: https://github.com/coder/coder/actions/runs/3057047622/jobs/4931863163")
|
||||
client := coderdtest.New(t, nil)
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
me, err := client.User(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
orgID := me.OrganizationIDs[0]
|
||||
|
||||
total := 10
|
||||
users := make([]codersdk.User, 0, total)
|
||||
// Create users
|
||||
for i := 0; i < total; i++ {
|
||||
email := fmt.Sprintf("%d@coder.com", i)
|
||||
username := fmt.Sprintf("user%d", i)
|
||||
user, err := client.CreateUserWithOrgs(ctx, codersdk.CreateUserRequestWithOrgs{
|
||||
Email: email,
|
||||
Username: username,
|
||||
Password: "MySecurePassword!",
|
||||
OrganizationIDs: []uuid.UUID{orgID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
users = append(users, user)
|
||||
}
|
||||
sortUsers(users)
|
||||
deletedUser := users[2]
|
||||
expected := users[3:8]
|
||||
_, err = client.UpdateUserStatus(ctx, deletedUser.ID.String(), codersdk.UserStatusSuspended)
|
||||
require.NoError(t, err, "suspend user")
|
||||
|
||||
page, err := client.Users(ctx, codersdk.UsersRequest{
|
||||
Pagination: codersdk.Pagination{
|
||||
Limit: len(expected),
|
||||
AfterID: deletedUser.ID,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, page.Users, "expected page")
|
||||
}
|
||||
|
||||
func TestUserAutofillParameters(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("NotSelf", func(t *testing.T) {
|
||||
@@ -3381,13 +3333,6 @@ func assertPagination(ctx context.Context, t *testing.T, client *codersdk.Client
|
||||
}
|
||||
}
|
||||
|
||||
// sortUsers sorts by (created_at, id)
|
||||
func sortUsers(users []codersdk.User) {
|
||||
slices.SortFunc(users, func(a, b codersdk.User) int {
|
||||
return slice.Ascending(strings.ToLower(a.Username), strings.ToLower(b.Username))
|
||||
})
|
||||
}
|
||||
|
||||
func sortDatabaseUsers(users []database.User) {
|
||||
slices.SortFunc(users, func(a, b database.User) int {
|
||||
return slice.Ascending(strings.ToLower(a.Username), strings.ToLower(b.Username))
|
||||
|
||||
@@ -2080,355 +2080,6 @@ func TestWorkspaceFilterAllStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestWorkspaceFilter creates a set of workspaces, users, and organizations
|
||||
// to run various filters against for testing.
|
||||
func TestWorkspaceFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Manual tests still occur below, so this is safe to disable.
|
||||
t.Skip("This test is slow and flaky. See: https://github.com/coder/coder/issues/2854")
|
||||
// nolint:unused
|
||||
type coderUser struct {
|
||||
*codersdk.Client
|
||||
User codersdk.User
|
||||
Org codersdk.Organization
|
||||
}
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
first := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
users := make([]coderUser, 0)
|
||||
for i := 0; i < 10; i++ {
|
||||
userClient, user := coderdtest.CreateAnotherUser(t, client, first.OrganizationID, rbac.RoleOwner())
|
||||
|
||||
if i%3 == 0 {
|
||||
var err error
|
||||
user, err = client.UpdateUserProfile(ctx, user.ID.String(), codersdk.UpdateUserProfileRequest{
|
||||
Username: strings.ToUpper(user.Username),
|
||||
})
|
||||
require.NoError(t, err, "uppercase username")
|
||||
}
|
||||
|
||||
org, err := userClient.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
|
||||
Name: user.Username + "-org",
|
||||
})
|
||||
require.NoError(t, err, "create org")
|
||||
|
||||
users = append(users, coderUser{
|
||||
Client: userClient,
|
||||
User: user,
|
||||
Org: org,
|
||||
})
|
||||
}
|
||||
|
||||
type madeWorkspace struct {
|
||||
Owner codersdk.User
|
||||
Workspace codersdk.Workspace
|
||||
Template codersdk.Template
|
||||
}
|
||||
|
||||
availTemplates := make([]codersdk.Template, 0)
|
||||
allWorkspaces := make([]madeWorkspace, 0)
|
||||
upperTemplates := make([]string, 0)
|
||||
|
||||
// Create some random workspaces
|
||||
var count int
|
||||
for i, user := range users {
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.Org.ID, nil)
|
||||
|
||||
// Create a template & workspace in the user's org
|
||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||
|
||||
var template codersdk.Template
|
||||
if i%3 == 0 {
|
||||
template = coderdtest.CreateTemplate(t, client, user.Org.ID, version.ID, func(request *codersdk.CreateTemplateRequest) {
|
||||
request.Name = strings.ToUpper(request.Name)
|
||||
})
|
||||
upperTemplates = append(upperTemplates, template.Name)
|
||||
} else {
|
||||
template = coderdtest.CreateTemplate(t, client, user.Org.ID, version.ID)
|
||||
}
|
||||
|
||||
availTemplates = append(availTemplates, template)
|
||||
workspace := coderdtest.CreateWorkspace(t, user.Client, template.ID, func(request *codersdk.CreateWorkspaceRequest) {
|
||||
if count%3 == 0 {
|
||||
request.Name = strings.ToUpper(request.Name)
|
||||
}
|
||||
})
|
||||
allWorkspaces = append(allWorkspaces, madeWorkspace{
|
||||
Workspace: workspace,
|
||||
Template: template,
|
||||
Owner: user.User,
|
||||
})
|
||||
|
||||
// Make a workspace with a random template
|
||||
idx, _ := cryptorand.Intn(len(availTemplates))
|
||||
randTemplate := availTemplates[idx]
|
||||
randWorkspace := coderdtest.CreateWorkspace(t, user.Client, randTemplate.ID)
|
||||
allWorkspaces = append(allWorkspaces, madeWorkspace{
|
||||
Workspace: randWorkspace,
|
||||
Template: randTemplate,
|
||||
Owner: user.User,
|
||||
})
|
||||
}
|
||||
|
||||
// Make sure all workspaces are done. Do it after all are made
|
||||
for i, w := range allWorkspaces {
|
||||
latest := coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, w.Workspace.LatestBuild.ID)
|
||||
allWorkspaces[i].Workspace.LatestBuild = latest
|
||||
}
|
||||
|
||||
// --- Setup done ---
|
||||
testCases := []struct {
|
||||
Name string
|
||||
Filter codersdk.WorkspaceFilter
|
||||
// If FilterF is true, we include it in the expected results
|
||||
FilterF func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool
|
||||
}{
|
||||
{
|
||||
Name: "All",
|
||||
Filter: codersdk.WorkspaceFilter{},
|
||||
FilterF: func(_ codersdk.WorkspaceFilter, _ madeWorkspace) bool {
|
||||
return true
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Owner",
|
||||
Filter: codersdk.WorkspaceFilter{
|
||||
Owner: strings.ToUpper(users[2].User.Username),
|
||||
},
|
||||
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
|
||||
return strings.EqualFold(workspace.Owner.Username, f.Owner)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "TemplateName",
|
||||
Filter: codersdk.WorkspaceFilter{
|
||||
Template: strings.ToUpper(allWorkspaces[5].Template.Name),
|
||||
},
|
||||
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
|
||||
return strings.EqualFold(workspace.Template.Name, f.Template)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "UpperTemplateName",
|
||||
Filter: codersdk.WorkspaceFilter{
|
||||
Template: upperTemplates[0],
|
||||
},
|
||||
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
|
||||
return strings.EqualFold(workspace.Template.Name, f.Template)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Name",
|
||||
Filter: codersdk.WorkspaceFilter{
|
||||
// Use a common letter... one has to have this letter in it
|
||||
Name: "a",
|
||||
},
|
||||
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
|
||||
return strings.ContainsAny(workspace.Workspace.Name, "Aa")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Q-Owner/Name",
|
||||
Filter: codersdk.WorkspaceFilter{
|
||||
FilterQuery: allWorkspaces[5].Owner.Username + "/" + strings.ToUpper(allWorkspaces[5].Workspace.Name),
|
||||
},
|
||||
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
|
||||
if strings.EqualFold(workspace.Owner.Username, allWorkspaces[5].Owner.Username) &&
|
||||
strings.Contains(strings.ToLower(workspace.Workspace.Name), strings.ToLower(allWorkspaces[5].Workspace.Name)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Many filters",
|
||||
Filter: codersdk.WorkspaceFilter{
|
||||
Owner: allWorkspaces[3].Owner.Username,
|
||||
Template: allWorkspaces[3].Template.Name,
|
||||
Name: allWorkspaces[3].Workspace.Name,
|
||||
},
|
||||
FilterF: func(f codersdk.WorkspaceFilter, workspace madeWorkspace) bool {
|
||||
if strings.EqualFold(workspace.Owner.Username, f.Owner) &&
|
||||
strings.Contains(strings.ToLower(workspace.Workspace.Name), strings.ToLower(f.Name)) &&
|
||||
strings.EqualFold(workspace.Template.Name, f.Template) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testCases {
|
||||
t.Run(c.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
workspaces, err := client.Workspaces(ctx, c.Filter)
|
||||
require.NoError(t, err, "fetch workspaces")
|
||||
|
||||
exp := make([]codersdk.Workspace, 0)
|
||||
for _, made := range allWorkspaces {
|
||||
if c.FilterF(c.Filter, made) {
|
||||
exp = append(exp, made.Workspace)
|
||||
}
|
||||
}
|
||||
require.ElementsMatch(t, exp, workspaces, "expected workspaces returned")
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("Shared", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dv := coderdtest.DeploymentValues(t)
|
||||
|
||||
var (
|
||||
client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
DeploymentValues: dv,
|
||||
})
|
||||
orgOwner = coderdtest.CreateFirstUser(t, client)
|
||||
_, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
|
||||
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OwnerID: workspaceOwner.ID,
|
||||
OrganizationID: orgOwner.OrganizationID,
|
||||
}).Do().Workspace
|
||||
_ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OwnerID: workspaceOwner.ID,
|
||||
OrganizationID: orgOwner.OrganizationID,
|
||||
}).Do().Workspace
|
||||
_, toShareWithUser = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
||||
ctx = testutil.Context(t, testutil.WaitMedium)
|
||||
)
|
||||
|
||||
client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
|
||||
UserRoles: map[string]codersdk.WorkspaceRole{
|
||||
toShareWithUser.ID.String(): codersdk.WorkspaceRoleUse,
|
||||
},
|
||||
})
|
||||
|
||||
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
|
||||
Shared: ptr.Ref(true),
|
||||
})
|
||||
require.NoError(t, err, "fetch workspaces")
|
||||
require.Equal(t, 1, workspaces.Count, "expected only one workspace")
|
||||
require.Equal(t, workspaces.Workspaces[0].ID, sharedWorkspace.ID)
|
||||
})
|
||||
|
||||
t.Run("NotShared", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dv := coderdtest.DeploymentValues(t)
|
||||
|
||||
var (
|
||||
client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
DeploymentValues: dv,
|
||||
})
|
||||
orgOwner = coderdtest.CreateFirstUser(t, client)
|
||||
_, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
|
||||
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OwnerID: workspaceOwner.ID,
|
||||
OrganizationID: orgOwner.OrganizationID,
|
||||
}).Do().Workspace
|
||||
notSharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OwnerID: workspaceOwner.ID,
|
||||
OrganizationID: orgOwner.OrganizationID,
|
||||
}).Do().Workspace
|
||||
_, toShareWithUser = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
||||
ctx = testutil.Context(t, testutil.WaitMedium)
|
||||
)
|
||||
|
||||
client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
|
||||
UserRoles: map[string]codersdk.WorkspaceRole{
|
||||
toShareWithUser.ID.String(): codersdk.WorkspaceRoleUse,
|
||||
},
|
||||
})
|
||||
|
||||
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
|
||||
Shared: ptr.Ref(false),
|
||||
})
|
||||
require.NoError(t, err, "fetch workspaces")
|
||||
require.Equal(t, 1, workspaces.Count, "expected only one workspace")
|
||||
require.Equal(t, workspaces.Workspaces[0].ID, notSharedWorkspace.ID)
|
||||
})
|
||||
|
||||
t.Run("SharedWithUserByID", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dv := coderdtest.DeploymentValues(t)
|
||||
|
||||
var (
|
||||
client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
DeploymentValues: dv,
|
||||
})
|
||||
orgOwner = coderdtest.CreateFirstUser(t, client)
|
||||
_, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
|
||||
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OwnerID: workspaceOwner.ID,
|
||||
OrganizationID: orgOwner.OrganizationID,
|
||||
}).Do().Workspace
|
||||
_ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OwnerID: workspaceOwner.ID,
|
||||
OrganizationID: orgOwner.OrganizationID,
|
||||
}).Do().Workspace
|
||||
_, toShareWithUser = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
||||
ctx = testutil.Context(t, testutil.WaitMedium)
|
||||
)
|
||||
|
||||
client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
|
||||
UserRoles: map[string]codersdk.WorkspaceRole{
|
||||
toShareWithUser.ID.String(): codersdk.WorkspaceRoleUse,
|
||||
},
|
||||
})
|
||||
|
||||
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
|
||||
SharedWithUser: toShareWithUser.ID.String(),
|
||||
})
|
||||
require.NoError(t, err, "fetch workspaces")
|
||||
require.Equal(t, 1, workspaces.Count, "expected only one workspace")
|
||||
require.Equal(t, workspaces.Workspaces[0].ID, sharedWorkspace.ID)
|
||||
})
|
||||
|
||||
t.Run("SharedWithUserByUsername", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dv := coderdtest.DeploymentValues(t)
|
||||
|
||||
var (
|
||||
client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
DeploymentValues: dv,
|
||||
})
|
||||
orgOwner = coderdtest.CreateFirstUser(t, client)
|
||||
_, workspaceOwner = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID, rbac.ScopedRoleOrgAuditor(orgOwner.OrganizationID))
|
||||
sharedWorkspace = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OwnerID: workspaceOwner.ID,
|
||||
OrganizationID: orgOwner.OrganizationID,
|
||||
}).Do().Workspace
|
||||
_ = dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OwnerID: workspaceOwner.ID,
|
||||
OrganizationID: orgOwner.OrganizationID,
|
||||
}).Do().Workspace
|
||||
_, toShareWithUser = coderdtest.CreateAnotherUser(t, client, orgOwner.OrganizationID)
|
||||
ctx = testutil.Context(t, testutil.WaitMedium)
|
||||
)
|
||||
|
||||
client.UpdateWorkspaceACL(ctx, sharedWorkspace.ID, codersdk.UpdateWorkspaceACL{
|
||||
UserRoles: map[string]codersdk.WorkspaceRole{
|
||||
toShareWithUser.ID.String(): codersdk.WorkspaceRoleUse,
|
||||
},
|
||||
})
|
||||
|
||||
workspaces, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{
|
||||
SharedWithUser: toShareWithUser.Username,
|
||||
})
|
||||
require.NoError(t, err, "fetch workspaces")
|
||||
require.Equal(t, 1, workspaces.Count, "expected only one workspace")
|
||||
require.Equal(t, workspaces.Workspaces[0].ID, sharedWorkspace.ID)
|
||||
})
|
||||
}
|
||||
|
||||
// TestWorkspaceFilterManual runs some specific setups with basic checks.
|
||||
func TestWorkspaceFilterManual(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -75,15 +75,6 @@ func TrackerWithLogger(log slog.Logger) TrackerOption {
|
||||
}
|
||||
}
|
||||
|
||||
// TrackerWithFlushInterval allows configuring the flush interval of Tracker.
|
||||
func TrackerWithFlushInterval(d time.Duration) TrackerOption {
|
||||
return func(h *UsageTracker) {
|
||||
ticker := time.NewTicker(d)
|
||||
h.tickCh = ticker.C
|
||||
h.stopTick = ticker.Stop
|
||||
}
|
||||
}
|
||||
|
||||
// TrackerWithTickFlush allows passing two channels: one that reads
|
||||
// a time.Time, and one that returns the number of marked workspaces
|
||||
// every time Tracker flushes.
|
||||
|
||||
@@ -94,15 +94,6 @@ type Worker struct {
|
||||
// WorkerOption configures a Worker.
|
||||
type WorkerOption func(*Worker)
|
||||
|
||||
// WithTickTimeout sets the maximum duration for a single tick.
|
||||
func WithTickTimeout(d time.Duration) WorkerOption {
|
||||
return func(w *Worker) {
|
||||
if d > 0 {
|
||||
w.tickTimeout = d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewWorker creates a Worker with default batch size and interval.
|
||||
func NewWorker(
|
||||
store Store,
|
||||
|
||||
@@ -259,11 +259,6 @@ func (opts *LicenseOptions) Generate(t *testing.T) string {
|
||||
return GenerateLicense(t, *opts)
|
||||
}
|
||||
|
||||
// AddFullLicense generates a license with all features enabled.
|
||||
func AddFullLicense(t *testing.T, client *codersdk.Client) codersdk.License {
|
||||
return AddLicense(t, client, LicenseOptions{AllFeatures: true})
|
||||
}
|
||||
|
||||
// AddLicense generates a new license with the options provided and inserts it.
|
||||
func AddLicense(t *testing.T, client *codersdk.Client, options LicenseOptions) codersdk.License {
|
||||
l, err := client.AddLicense(context.Background(), codersdk.AddLicenseRequest{
|
||||
|
||||
@@ -100,14 +100,6 @@ func WithBatchSize(size int) DBBatcherOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithFlushInterval sets how frequently the batcher flushes to the
|
||||
// database.
|
||||
func WithFlushInterval(d time.Duration) DBBatcherOption {
|
||||
return func(b *DBBatcher) {
|
||||
b.interval = d
|
||||
}
|
||||
}
|
||||
|
||||
// WithClock sets the clock, useful for testing.
|
||||
func WithClock(clock quartz.Clock) DBBatcherOption {
|
||||
return func(b *DBBatcher) {
|
||||
|
||||
@@ -709,20 +709,6 @@ data "coder_parameter" "{{ .Name }}" {
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
func WithResources(resources []*proto.Resource) *Responses {
|
||||
return &Responses{
|
||||
Parse: ParseComplete,
|
||||
ProvisionInit: InitComplete,
|
||||
ProvisionApply: []*proto.Response{{Type: &proto.Response_Apply{Apply: &proto.ApplyComplete{}}}},
|
||||
ProvisionGraph: []*proto.Response{{Type: &proto.Response_Graph{Graph: &proto.GraphComplete{
|
||||
Resources: resources,
|
||||
}}}},
|
||||
ProvisionPlan: []*proto.Response{{Type: &proto.Response_Plan{Plan: &proto.PlanComplete{
|
||||
Plan: []byte("{}"),
|
||||
}}}},
|
||||
}
|
||||
}
|
||||
|
||||
func WithExtraFiles(extraFiles map[string][]byte) *Responses {
|
||||
return &Responses{
|
||||
Parse: ParseComplete,
|
||||
|
||||
@@ -109,15 +109,6 @@ func (p *PTY) WriteLine(str string) {
|
||||
require.NoError(p.t, err, "write line failed")
|
||||
}
|
||||
|
||||
// Named sets the PTY name in the logs. Defaults to "cmd". Make sure you set this before anything starts writing to the
|
||||
// pty, or it may not be named consistently. E.g.
|
||||
//
|
||||
// p := New(t).Named("myCmd")
|
||||
func (p *PTY) Named(name string) *PTY {
|
||||
p.Rename(name)
|
||||
return p
|
||||
}
|
||||
|
||||
type PTYCmd struct {
|
||||
expecter.Expecter
|
||||
pty.PTYCmd
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package autostart
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
@@ -92,104 +90,6 @@ func percentile(sorted []time.Duration, p float64) time.Duration {
|
||||
return sorted[index]
|
||||
}
|
||||
|
||||
// PrintText writes the results in a human-readable text format.
|
||||
func (r RunResults) PrintText(w io.Writer) {
|
||||
_, _ = fmt.Fprintf(w, "Autostart Scale Test Results\n")
|
||||
_, _ = fmt.Fprintf(w, "=============================\n\n")
|
||||
|
||||
_, _ = fmt.Fprintf(w, "Total Runs: %d\n", r.TotalRuns)
|
||||
_, _ = fmt.Fprintf(w, "Successful: %d\n", r.SuccessfulRuns)
|
||||
_, _ = fmt.Fprintf(w, "Failed: %d\n\n", r.FailedRuns)
|
||||
|
||||
if r.SuccessfulRuns > 0 {
|
||||
_, _ = fmt.Fprintf(w, "End-to-End Latency (Config → Completion)\n")
|
||||
_, _ = fmt.Fprintf(w, "-----------------------------------------\n")
|
||||
_, _ = fmt.Fprintf(w, "P50: %v\n", r.EndToEndLatencyP50.Round(time.Millisecond))
|
||||
_, _ = fmt.Fprintf(w, "P95: %v\n", r.EndToEndLatencyP95.Round(time.Millisecond))
|
||||
_, _ = fmt.Fprintf(w, "P99: %v\n\n", r.EndToEndLatencyP99.Round(time.Millisecond))
|
||||
|
||||
_, _ = fmt.Fprintf(w, "Trigger to Completion Latency (Scheduled Time → Completion)\n")
|
||||
_, _ = fmt.Fprintf(w, "------------------------------------------------------------\n")
|
||||
_, _ = fmt.Fprintf(w, "P50: %v\n", r.TriggerToCompletionP50.Round(time.Millisecond))
|
||||
_, _ = fmt.Fprintf(w, "P95: %v\n", r.TriggerToCompletionP95.Round(time.Millisecond))
|
||||
_, _ = fmt.Fprintf(w, "P99: %v\n\n", r.TriggerToCompletionP99.Round(time.Millisecond))
|
||||
}
|
||||
|
||||
if r.FailedRuns > 0 {
|
||||
_, _ = fmt.Fprintf(w, "Failed Runs\n")
|
||||
_, _ = fmt.Fprintf(w, "-----------\n")
|
||||
for _, run := range r.Runs {
|
||||
if !run.Success {
|
||||
_, _ = fmt.Fprintf(w, "- %s (%s): %s\n", run.WorkspaceName, run.WorkspaceID, run.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler to provide custom JSON output.
|
||||
func (r RunResults) MarshalJSON() ([]byte, error) {
|
||||
// Convert durations to milliseconds for JSON output.
|
||||
type jsonResults struct {
|
||||
TotalRuns int `json:"total_runs"`
|
||||
SuccessfulRuns int `json:"successful_runs"`
|
||||
FailedRuns int `json:"failed_runs"`
|
||||
|
||||
EndToEndLatencyP50MS int64 `json:"end_to_end_latency_p50_ms"`
|
||||
EndToEndLatencyP95MS int64 `json:"end_to_end_latency_p95_ms"`
|
||||
EndToEndLatencyP99MS int64 `json:"end_to_end_latency_p99_ms"`
|
||||
|
||||
TriggerToCompletionP50MS int64 `json:"trigger_to_completion_p50_ms"`
|
||||
TriggerToCompletionP95MS int64 `json:"trigger_to_completion_p95_ms"`
|
||||
TriggerToCompletionP99MS int64 `json:"trigger_to_completion_p99_ms"`
|
||||
|
||||
Runs []struct {
|
||||
WorkspaceID string `json:"workspace_id"`
|
||||
WorkspaceName string `json:"workspace_name"`
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error,omitempty"`
|
||||
|
||||
EndToEndLatencyMS int64 `json:"end_to_end_latency_ms"`
|
||||
TriggerToCompletionMS int64 `json:"trigger_to_completion_ms"`
|
||||
} `json:"runs"`
|
||||
}
|
||||
|
||||
jr := jsonResults{
|
||||
TotalRuns: r.TotalRuns,
|
||||
SuccessfulRuns: r.SuccessfulRuns,
|
||||
FailedRuns: r.FailedRuns,
|
||||
|
||||
EndToEndLatencyP50MS: r.EndToEndLatencyP50.Milliseconds(),
|
||||
EndToEndLatencyP95MS: r.EndToEndLatencyP95.Milliseconds(),
|
||||
EndToEndLatencyP99MS: r.EndToEndLatencyP99.Milliseconds(),
|
||||
|
||||
TriggerToCompletionP50MS: r.TriggerToCompletionP50.Milliseconds(),
|
||||
TriggerToCompletionP95MS: r.TriggerToCompletionP95.Milliseconds(),
|
||||
TriggerToCompletionP99MS: r.TriggerToCompletionP99.Milliseconds(),
|
||||
}
|
||||
|
||||
for _, run := range r.Runs {
|
||||
jr.Runs = append(jr.Runs, struct {
|
||||
WorkspaceID string `json:"workspace_id"`
|
||||
WorkspaceName string `json:"workspace_name"`
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error,omitempty"`
|
||||
|
||||
EndToEndLatencyMS int64 `json:"end_to_end_latency_ms"`
|
||||
TriggerToCompletionMS int64 `json:"trigger_to_completion_ms"`
|
||||
}{
|
||||
WorkspaceID: run.WorkspaceID.String(),
|
||||
WorkspaceName: run.WorkspaceName,
|
||||
Success: run.Success,
|
||||
Error: run.Error,
|
||||
|
||||
EndToEndLatencyMS: run.EndToEndLatency().Milliseconds(),
|
||||
TriggerToCompletionMS: run.TriggerToCompletionLatency().Milliseconds(),
|
||||
})
|
||||
}
|
||||
|
||||
return json.Marshal(jr)
|
||||
}
|
||||
|
||||
// ToHarnessResults converts autostart-specific results into the standard
|
||||
// harness.Results format for use with existing output functions.
|
||||
func (r RunResults) ToHarnessResults() harness.Results {
|
||||
|
||||
@@ -1500,12 +1500,6 @@ func NewController(logger slog.Logger, dialer ControlProtocolDialer, opts ...Con
|
||||
|
||||
type ControllerOpt func(*Controller)
|
||||
|
||||
func WithTestClock(clock quartz.Clock) ControllerOpt {
|
||||
return func(c *Controller) {
|
||||
c.clock = clock
|
||||
}
|
||||
}
|
||||
|
||||
func WithGracefulTimeout(timeout time.Duration) ControllerOpt {
|
||||
return func(c *Controller) {
|
||||
c.gracefulTimeout = timeout
|
||||
|
||||
@@ -79,27 +79,6 @@ func SoftTryReceive[A any](ctx context.Context, t testing.TB, c <-chan A) (A, bo
|
||||
}
|
||||
}
|
||||
|
||||
// AssertReceive will receive a value from the chan and return it. If the
|
||||
// context expires or the channel is closed before a value can be received,
|
||||
// it will mark the test as failed but continue execution.
|
||||
// The second return value indicates whether the receive was successful.
|
||||
//
|
||||
// Safety: can be called from any goroutine.
|
||||
func AssertReceive[A any](ctx context.Context, t testing.TB, c <-chan A) (A, bool) {
|
||||
t.Helper()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
assert.Fail(t, "AssertReceive: context expired")
|
||||
var a A
|
||||
return a, false
|
||||
case a, ok := <-c:
|
||||
if !ok {
|
||||
assert.Fail(t, "AssertReceive: channel closed")
|
||||
}
|
||||
return a, ok
|
||||
}
|
||||
}
|
||||
|
||||
// AssertSend will send the given value over the chan and then return. If
|
||||
// the context expires before the send succeeds, it will mark the test as failed
|
||||
// but continue execution.
|
||||
|
||||
@@ -284,24 +284,6 @@ func (e *Expecter) Peek(ctx context.Context, n int) []byte {
|
||||
return slices.Clone(out)
|
||||
}
|
||||
|
||||
//nolint:govet // We don't care about conforming to ReadRune() (rune, int, error).
|
||||
func (e *Expecter) ReadRune(ctx context.Context) rune {
|
||||
e.t.Helper()
|
||||
|
||||
var r rune
|
||||
err := e.doMatchWithDeadline(ctx, "ReadRune", func(rd *bufio.Reader) error {
|
||||
var err error
|
||||
r, _, err = rd.ReadRune()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
e.fatalf("read error", "%v (wanted rune; got %q)", err, r)
|
||||
return 0
|
||||
}
|
||||
e.Logf("matched rune = %q", r)
|
||||
return r
|
||||
}
|
||||
|
||||
func (e *Expecter) ReadLine(ctx context.Context) string {
|
||||
e.t.Helper()
|
||||
|
||||
|
||||
@@ -29,11 +29,6 @@ func PromGaugeAssertion(t testing.TB, metrics []*dto.MetricFamily, assert func(i
|
||||
return assert(getValue(t, metrics, gaugeKind, name, labels...))
|
||||
}
|
||||
|
||||
func PromCounterAssertion(t testing.TB, metrics []*dto.MetricFamily, assert func(in float64) bool, name string, labels ...string) bool {
|
||||
t.Helper()
|
||||
return assert(getValue(t, metrics, counterKind, name, labels...))
|
||||
}
|
||||
|
||||
func PromCounterGathered(t testing.TB, metrics []*dto.MetricFamily, name string, labels ...string) bool {
|
||||
t.Helper()
|
||||
return getMetric(t, metrics, name, labels...) != nil
|
||||
|
||||
@@ -47,14 +47,6 @@ func NewWriterAttachedToSSHSession(t *testing.T, l slog.Logger, session *ssh.Ses
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) Write(r rune) {
|
||||
w.t.Helper()
|
||||
_, err := w.w.Write([]byte{byte(r)})
|
||||
if assert.NoError(w.t, err, "write failed") {
|
||||
w.l.Debug(context.Background(), "wrote rune", slog.F("rune", r))
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) WriteLine(str string) {
|
||||
w.t.Helper()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user