mirror of
https://github.com/tiennm99/claude-code-routine-trigger.git
synced 2026-10-14 12:16:40 +00:00
test: add unit tests for config, fire client, scheduler
This commit is contained in:
1 parent
9c4d7a532f
commit
e18d271970
3 files changed
+493
No files matched your search
@@ -0,0 +1,215 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func clearEnv(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
for _, k := range []string{"ROUTINE_FIRE_URL", "ROUTINE_FIRE_TOKEN", "CRON_SCHEDULE", "TZ", "TEXT_TEMPLATE", "LOG_LEVEL"} {
|
||||||
|
t.Setenv(k, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setBaseEnv(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
t.Setenv("ROUTINE_FIRE_URL", "https://example.test/fire")
|
||||||
|
t.Setenv("ROUTINE_FIRE_TOKEN", "tok")
|
||||||
|
t.Setenv("CRON_SCHEDULE", "0 9 * * *")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_Happy(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
setBaseEnv(t)
|
||||||
|
|
||||||
|
cfg, err := Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.Schedules) != 1 || cfg.Schedules[0] != "0 9 * * *" {
|
||||||
|
t.Fatalf("schedules = %v", cfg.Schedules)
|
||||||
|
}
|
||||||
|
if cfg.Location != time.UTC {
|
||||||
|
t.Fatalf("default tz should be UTC, got %v", cfg.Location)
|
||||||
|
}
|
||||||
|
if cfg.LogLevel != "info" {
|
||||||
|
t.Fatalf("default log level should be info, got %q", cfg.LogLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MultiCronSemicolon(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
setBaseEnv(t)
|
||||||
|
t.Setenv("CRON_SCHEDULE", "0 17 * * *;0 22 * * *")
|
||||||
|
|
||||||
|
cfg, err := Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.Schedules) != 2 {
|
||||||
|
t.Fatalf("want 2, got %d: %v", len(cfg.Schedules), cfg.Schedules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MultiCronNewline(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
setBaseEnv(t)
|
||||||
|
t.Setenv("CRON_SCHEDULE", "0 17 * * *\n0 22 * * *")
|
||||||
|
|
||||||
|
cfg, err := Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.Schedules) != 2 {
|
||||||
|
t.Fatalf("want 2, got %d: %v", len(cfg.Schedules), cfg.Schedules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MixedSeparatorsAndWhitespace(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
setBaseEnv(t)
|
||||||
|
t.Setenv("CRON_SCHEDULE", " 0 17 * * * ;\n 0 22 * * * ;;\n")
|
||||||
|
|
||||||
|
cfg, err := Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.Schedules) != 2 {
|
||||||
|
t.Fatalf("want 2 trimmed schedules, got %v", cfg.Schedules)
|
||||||
|
}
|
||||||
|
if cfg.Schedules[0] != "0 17 * * *" || cfg.Schedules[1] != "0 22 * * *" {
|
||||||
|
t.Fatalf("unexpected trimmed values: %v", cfg.Schedules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MissingFireURL(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
t.Setenv("ROUTINE_FIRE_TOKEN", "tok")
|
||||||
|
t.Setenv("CRON_SCHEDULE", "0 9 * * *")
|
||||||
|
|
||||||
|
_, err := Load()
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "ROUTINE_FIRE_URL") {
|
||||||
|
t.Fatalf("want missing url error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_MissingToken(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
t.Setenv("ROUTINE_FIRE_URL", "https://example.test/fire")
|
||||||
|
t.Setenv("CRON_SCHEDULE", "0 9 * * *")
|
||||||
|
|
||||||
|
_, err := Load()
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "ROUTINE_FIRE_TOKEN") {
|
||||||
|
t.Fatalf("want missing token error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_EmptyCron(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
t.Setenv("ROUTINE_FIRE_URL", "https://example.test/fire")
|
||||||
|
t.Setenv("ROUTINE_FIRE_TOKEN", "tok")
|
||||||
|
t.Setenv("CRON_SCHEDULE", "")
|
||||||
|
|
||||||
|
_, err := Load()
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "CRON_SCHEDULE") {
|
||||||
|
t.Fatalf("want missing cron error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_InvalidCron(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
setBaseEnv(t)
|
||||||
|
t.Setenv("CRON_SCHEDULE", "not-a-cron")
|
||||||
|
|
||||||
|
_, err := Load()
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "invalid cron") {
|
||||||
|
t.Fatalf("want invalid cron error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_InvalidTZ(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
setBaseEnv(t)
|
||||||
|
t.Setenv("TZ", "Atlantis/Lost")
|
||||||
|
|
||||||
|
_, err := Load()
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "invalid TZ") {
|
||||||
|
t.Fatalf("want invalid TZ error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_DefaultTemplate(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
setBaseEnv(t)
|
||||||
|
|
||||||
|
cfg, err := Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
out, err := renderTemplate(cfg, time.Date(2026, 5, 8, 12, 0, 0, 0, time.UTC), "0 9 * * *")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("render: %v", err)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(out, "Scheduled trigger at ") {
|
||||||
|
t.Fatalf("default template output unexpected: %q", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_CustomTemplate(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
setBaseEnv(t)
|
||||||
|
t.Setenv("TEXT_TEMPLATE", "hi {{.Cron}}")
|
||||||
|
|
||||||
|
cfg, err := Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
out, err := renderTemplate(cfg, time.Now(), "0 9 * * *")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("render: %v", err)
|
||||||
|
}
|
||||||
|
if out != "hi 0 9 * * *" {
|
||||||
|
t.Fatalf("custom template output: %q", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_InvalidTemplate(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
setBaseEnv(t)
|
||||||
|
t.Setenv("TEXT_TEMPLATE", "{{.broken")
|
||||||
|
|
||||||
|
_, err := Load()
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "invalid TEXT_TEMPLATE") {
|
||||||
|
t.Fatalf("want invalid template error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_LogLevelDefault(t *testing.T) {
|
||||||
|
clearEnv(t)
|
||||||
|
setBaseEnv(t)
|
||||||
|
|
||||||
|
cfg, err := Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if cfg.LogLevel != "info" {
|
||||||
|
t.Fatalf("got %q", cfg.LogLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderTemplate runs cfg.Template with the documented vars; mirror of
|
||||||
|
// FireClient.renderText for test isolation.
|
||||||
|
func renderTemplate(cfg *Config, now time.Time, expr string) (string, error) {
|
||||||
|
data := map[string]any{
|
||||||
|
"Now": now,
|
||||||
|
"LocalTime": now.In(cfg.Location).Format("2006-01-02 15:04 MST"),
|
||||||
|
"Cron": expr,
|
||||||
|
}
|
||||||
|
var sb strings.Builder
|
||||||
|
if err := cfg.Template.Execute(&sb, data); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return sb.String(), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func captureLogs() (*slog.Logger, *bytes.Buffer) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
h := slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})
|
||||||
|
return slog.New(h), &buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustTemplate(t *testing.T, raw string) *template.Template {
|
||||||
|
t.Helper()
|
||||||
|
tpl, err := template.New("t").Parse(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("template parse: %v", err)
|
||||||
|
}
|
||||||
|
return tpl
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFire_HappyPath(t *testing.T) {
|
||||||
|
var gotMethod, gotAuth, gotVersion, gotBeta, gotContentType string
|
||||||
|
var gotBody []byte
|
||||||
|
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
gotMethod = r.Method
|
||||||
|
gotAuth = r.Header.Get("Authorization")
|
||||||
|
gotVersion = r.Header.Get("anthropic-version")
|
||||||
|
gotBeta = r.Header.Get("anthropic-beta")
|
||||||
|
gotContentType = r.Header.Get("Content-Type")
|
||||||
|
gotBody, _ = io.ReadAll(r.Body)
|
||||||
|
_, _ = w.Write([]byte(`{"type":"routine_fire","claude_code_session_id":"sess_1","claude_code_session_url":"https://claude.ai/code/sess_1"}`))
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
logger, logbuf := captureLogs()
|
||||||
|
c := &FireClient{
|
||||||
|
URL: srv.URL,
|
||||||
|
Token: "tok",
|
||||||
|
HTTP: &http.Client{Timeout: 5 * time.Second},
|
||||||
|
Template: mustTemplate(t, "fire {{.Cron}}"),
|
||||||
|
TZ: time.UTC,
|
||||||
|
Log: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Fire(context.Background(), "0 9 * * *"); err != nil {
|
||||||
|
t.Fatalf("Fire returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotMethod != http.MethodPost {
|
||||||
|
t.Errorf("method = %q", gotMethod)
|
||||||
|
}
|
||||||
|
if gotAuth != "Bearer tok" {
|
||||||
|
t.Errorf("auth = %q", gotAuth)
|
||||||
|
}
|
||||||
|
if gotVersion != headerVersion {
|
||||||
|
t.Errorf("anthropic-version = %q", gotVersion)
|
||||||
|
}
|
||||||
|
if gotBeta != headerBeta {
|
||||||
|
t.Errorf("anthropic-beta = %q", gotBeta)
|
||||||
|
}
|
||||||
|
if gotContentType != "application/json" {
|
||||||
|
t.Errorf("content-type = %q", gotContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body fireRequest
|
||||||
|
if err := json.Unmarshal(gotBody, &body); err != nil {
|
||||||
|
t.Fatalf("body unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if body.Text != "fire 0 9 * * *" {
|
||||||
|
t.Errorf("body.text = %q", body.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(logbuf.String(), "fire ok") {
|
||||||
|
t.Errorf("expected 'fire ok' log, got: %s", logbuf.String())
|
||||||
|
}
|
||||||
|
if !strings.Contains(logbuf.String(), "https://claude.ai/code/sess_1") {
|
||||||
|
t.Errorf("expected session url in log, got: %s", logbuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFire_NonOK(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
_, _ = w.Write([]byte(`{"error":"unauthorized"}`))
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
logger, logbuf := captureLogs()
|
||||||
|
c := &FireClient{
|
||||||
|
URL: srv.URL,
|
||||||
|
Token: "tok",
|
||||||
|
HTTP: &http.Client{Timeout: 5 * time.Second},
|
||||||
|
Template: mustTemplate(t, "x"),
|
||||||
|
TZ: time.UTC,
|
||||||
|
Log: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Fire(context.Background(), "0 9 * * *"); err != nil {
|
||||||
|
t.Fatalf("Fire should swallow non-2xx, got error: %v", err)
|
||||||
|
}
|
||||||
|
out := logbuf.String()
|
||||||
|
if !strings.Contains(out, "non-2xx") {
|
||||||
|
t.Errorf("expected non-2xx log, got: %s", out)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "401") {
|
||||||
|
t.Errorf("expected status 401 in log, got: %s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFire_NetworkError(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||||
|
srv.Close() // close immediately so requests fail
|
||||||
|
|
||||||
|
logger, logbuf := captureLogs()
|
||||||
|
c := &FireClient{
|
||||||
|
URL: srv.URL,
|
||||||
|
Token: "tok",
|
||||||
|
HTTP: &http.Client{Timeout: 1 * time.Second},
|
||||||
|
Template: mustTemplate(t, "x"),
|
||||||
|
TZ: time.UTC,
|
||||||
|
Log: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Fire(context.Background(), "0 9 * * *"); err != nil {
|
||||||
|
t.Fatalf("Fire should swallow network error, got: %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(logbuf.String(), "fire request failed") {
|
||||||
|
t.Errorf("expected request-failed log, got: %s", logbuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFire_TemplateUsesNowFunc(t *testing.T) {
|
||||||
|
var gotBody []byte
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
gotBody, _ = io.ReadAll(r.Body)
|
||||||
|
_, _ = w.Write([]byte(`{}`))
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
fixed := time.Date(2026, 5, 8, 23, 30, 0, 0, time.UTC)
|
||||||
|
logger, _ := captureLogs()
|
||||||
|
c := &FireClient{
|
||||||
|
URL: srv.URL,
|
||||||
|
Token: "tok",
|
||||||
|
HTTP: &http.Client{Timeout: 5 * time.Second},
|
||||||
|
Template: mustTemplate(t, "{{.LocalTime}}"),
|
||||||
|
TZ: time.UTC,
|
||||||
|
Log: logger,
|
||||||
|
NowFunc: func() time.Time { return fixed },
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Fire(context.Background(), "0 9 * * *"); err != nil {
|
||||||
|
t.Fatalf("Fire: %v", err)
|
||||||
|
}
|
||||||
|
var body fireRequest
|
||||||
|
if err := json.Unmarshal(gotBody, &body); err != nil {
|
||||||
|
t.Fatalf("unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if body.Text != "2026-05-08 23:30 UTC" {
|
||||||
|
t.Errorf("body.text = %q", body.Text)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScheduler_RegistersAllEntries(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, _ = w.Write([]byte(`{}`))
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
tpl, _ := template.New("t").Parse("x")
|
||||||
|
cfg := &Config{
|
||||||
|
FireURL: srv.URL,
|
||||||
|
Token: "tok",
|
||||||
|
Schedules: []string{"0 17 * * *", "0 22 * * *", "*/5 * * * *"},
|
||||||
|
Location: time.UTC,
|
||||||
|
Template: tpl,
|
||||||
|
LogLevel: "info",
|
||||||
|
}
|
||||||
|
logger, _ := captureLogs()
|
||||||
|
fire := &FireClient{
|
||||||
|
URL: srv.URL,
|
||||||
|
Token: "tok",
|
||||||
|
HTTP: &http.Client{Timeout: 1 * time.Second},
|
||||||
|
Template: tpl,
|
||||||
|
TZ: time.UTC,
|
||||||
|
Log: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := NewScheduler(cfg, fire, logger)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewScheduler: %v", err)
|
||||||
|
}
|
||||||
|
if got := s.EntryCount(); got != 3 {
|
||||||
|
t.Fatalf("EntryCount = %d, want 3", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScheduler_StartStop(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, _ = w.Write([]byte(`{}`))
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
tpl, _ := template.New("t").Parse("x")
|
||||||
|
cfg := &Config{
|
||||||
|
FireURL: srv.URL,
|
||||||
|
Token: "tok",
|
||||||
|
Schedules: []string{"0 17 * * *"},
|
||||||
|
Location: time.UTC,
|
||||||
|
Template: tpl,
|
||||||
|
LogLevel: "info",
|
||||||
|
}
|
||||||
|
logger, _ := captureLogs()
|
||||||
|
fire := &FireClient{
|
||||||
|
URL: srv.URL,
|
||||||
|
Token: "tok",
|
||||||
|
HTTP: &http.Client{Timeout: 1 * time.Second},
|
||||||
|
Template: tpl,
|
||||||
|
TZ: time.UTC,
|
||||||
|
Log: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := NewScheduler(cfg, fire, logger)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewScheduler: %v", err)
|
||||||
|
}
|
||||||
|
s.Start()
|
||||||
|
|
||||||
|
stopCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
s.Stop(stopCtx)
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
t.Fatal("Stop did not return within 3s")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewLogger_UnknownLevelFallsBackToInfo(t *testing.T) {
|
||||||
|
l := newLogger("zoinks")
|
||||||
|
if l == nil {
|
||||||
|
t.Fatal("logger is nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewLogger_KnownLevels(t *testing.T) {
|
||||||
|
for _, lvl := range []string{"", "debug", "info", "warn", "warning", "error", "ERROR", " Debug "} {
|
||||||
|
if l := newLogger(lvl); l == nil {
|
||||||
|
t.Fatalf("nil logger for %q", lvl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user