snapshot of https://github.com/traggo/server.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
build / build (push) Failing after 3s

This commit is contained in:
upstream-mirror
2026-09-16 05:24:45 +05:00
commit cda7c1a474
272 changed files with 31973 additions and 0 deletions

270
test/db.go Normal file
View File

@@ -0,0 +1,270 @@
package test
import (
"fmt"
"github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert"
"github.com/traggo/server/database"
"github.com/traggo/server/model"
)
// InMemoryDB create a in memory database for testing.
func InMemoryDB(t assert.TestingT) *Database {
db, err := database.New("sqlite3", "file::memory:?mode=memory&cache=shared")
assert.Nil(t, err)
return &Database{DB: db, t: t}
}
// Database wraps the gorm.DB and provides helper methods
type Database struct {
*gorm.DB
t assert.TestingT
}
// User creates a user
func (d *Database) User(id int) *UserWithDatabase {
user := d.NewUser(id, fmt.Sprint("test", id), false)
return &UserWithDatabase{
User: user,
DB: d.DB,
t: d.t,
}
}
// UserWithDatabase wraps gorm.DB and provides helper methods
type UserWithDatabase struct {
User model.User
*gorm.DB
t assert.TestingT
}
// NewDevice creates a device.
func (d *UserWithDatabase) NewDevice(id int, token string, name string) model.Device {
device := model.Device{
ID: id,
Token: token,
UserID: d.User.ID,
Name: name,
Type: model.TypeNoExpiry,
ActiveAt: Time("2009-06-30T18:30:00Z"),
CreatedAt: Time("2009-06-30T18:30:00Z"),
}
d.Create(&device)
return device
}
// NewTagDefinition creates a tag definition.
func (d *UserWithDatabase) NewTagDefinition(key string) model.TagDefinition {
tagDefinition := model.TagDefinition{
UserID: d.User.ID,
Key: key,
}
d.Create(&tagDefinition)
return tagDefinition
}
// AssertHasTagDefinition asserts if a tag definition exists.
func (d *UserWithDatabase) AssertHasTagDefinition(key string, exist bool) *UserWithDatabase {
existActual := !d.DB.
Where(&model.TagDefinition{Key: key, UserID: d.User.ID}).
Find(new(model.TagDefinition)).
RecordNotFound()
assert.True(d.t, exist == existActual)
return d
}
// AssertHasDevice asserts if a device exists.
func (d *UserWithDatabase) AssertHasDevice(name string, exist bool) *UserWithDatabase {
existActual := !d.DB.
Where(&model.Device{Name: name, UserID: d.User.ID}).
Find(new(model.Device)).
RecordNotFound()
assert.True(d.t, exist == existActual)
return d
}
// NewUser creates a user
func (d *Database) NewUser(id int, name string, admin bool) model.User {
return d.NewUserPass(id, name, []byte{1, 2, 3}, admin)
}
// NewUserPass creates a user
func (d *Database) NewUserPass(id int, name string, pass []byte, admin bool) model.User {
user := model.User{
ID: id,
Name: name,
Pass: pass,
Admin: admin,
}
d.Create(&user)
return user
}
// AssertExists asserts if the tag exists or not.
func (d *UserWithDatabase) AssertExists(exist bool) *UserWithDatabase {
existActual := !d.DB.
Where(&model.User{ID: d.User.ID}).
Find(new(model.User)).
RecordNotFound()
assert.True(d.t, exist == existActual)
return d
}
// RunningTimeSpan adds a time span without end.
func (d *UserWithDatabase) RunningTimeSpan(from string) *TimeSpanWithDatabase {
timeSpan := model.TimeSpan{
UserID: d.User.ID,
StartUserTime: ModelTime(from).OmitTimeZone(),
StartUTC: ModelTime(from).UTC(),
}
d.DB.Create(&timeSpan)
return &TimeSpanWithDatabase{
User: d.User,
TimeSpan: timeSpan,
DB: d.DB,
t: d.t,
}
}
// Dashboard adds a dashboard.
func (d *UserWithDatabase) Dashboard(name string) *DashboardWithDatabase {
dashboard := model.Dashboard{
UserID: d.User.ID,
Name: name,
}
d.DB.Create(&dashboard)
return &DashboardWithDatabase{
User: d.User,
Dashboard: dashboard,
DB: d.DB,
t: d.t,
}
}
// Range adds a range to the dashboard.
func (d *DashboardWithDatabase) Range(name string) *DashboardWithDatabase {
dbRange := model.DashboardRange{
DashboardID: d.Dashboard.ID,
Name: name,
From: "now-1d",
To: "now",
}
d.Dashboard.Ranges = append(d.Dashboard.Ranges, dbRange)
d.DB.Save(&dbRange)
return d
}
// Entry adds an entry to the dashboard.
func (d *DashboardWithDatabase) Entry(name string) *DashboardWithDatabase {
entry := model.DashboardEntry{
DashboardID: d.Dashboard.ID,
Title: name,
}
d.Dashboard.Entries = append(d.Dashboard.Entries, entry)
d.DB.Save(&entry)
return d
}
// AssertExists asserts if the dashboard exists or not.
func (d *DashboardWithDatabase) AssertExists(exist bool) *DashboardWithDatabase {
existActual := !d.DB.
Where(&model.Dashboard{ID: d.Dashboard.ID}).
Find(new(model.Dashboard)).
RecordNotFound()
assert.True(d.t, exist == existActual)
return d
}
// AssertHasEntry asserts if the entry exists or not.
func (d *DashboardWithDatabase) AssertHasEntry(name string, exist bool) *DashboardWithDatabase {
existActual := !d.DB.
Where(&model.DashboardEntry{DashboardID: d.Dashboard.ID, Title: name}).
Find(new(model.DashboardEntry)).
RecordNotFound()
assert.True(d.t, exist == existActual)
return d
}
// AssertHasRange asserts if the range exists or not.
func (d *DashboardWithDatabase) AssertHasRange(name string, exist bool) *DashboardWithDatabase {
existActual := !d.DB.
Where(&model.DashboardRange{DashboardID: d.Dashboard.ID, Name: name}).
Find(new(model.DashboardRange)).
RecordNotFound()
assert.True(d.t, exist == existActual)
return d
}
// TimeSpan adds a time span.
func (d *UserWithDatabase) TimeSpan(from string, to string) *TimeSpanWithDatabase {
wrapper := d.RunningTimeSpan(from)
userTime := ModelTime(to).OmitTimeZone()
wrapper.TimeSpan.EndUserTime = &userTime
utc := ModelTime(to).UTC()
wrapper.TimeSpan.EndUTC = &utc
d.DB.Save(&wrapper.TimeSpan)
return wrapper
}
// TimeSpanWithDatabase wraps gorm.DB and provides helper methods
type TimeSpanWithDatabase struct {
User model.User
TimeSpan model.TimeSpan
t assert.TestingT
*gorm.DB
}
// AssertHasTag asserts if the tag exists or not.
func (d *TimeSpanWithDatabase) AssertHasTag(key, value string, exist bool) *TimeSpanWithDatabase {
existActual := !d.DB.
Where(&model.TimeSpanTag{Key: key, StringValue: value, TimeSpanID: d.TimeSpan.ID}).
Find(new(model.TimeSpanTag)).
RecordNotFound()
assert.True(d.t, exist == existActual)
return d
}
// AssertHasTagIgnoreValue asserts if the tag exists or not.
func (d *TimeSpanWithDatabase) AssertHasTagIgnoreValue(key string, exist bool) *TimeSpanWithDatabase {
existActual := !d.DB.
Where(&model.TimeSpanTag{Key: key, TimeSpanID: d.TimeSpan.ID}).
Find(new(model.TimeSpanTag)).
RecordNotFound()
assert.True(d.t, exist == existActual)
return d
}
// AssertExists asserts if the tag exists or not.
func (d *TimeSpanWithDatabase) AssertExists(exist bool) *TimeSpanWithDatabase {
existActual := !d.DB.
Where(&model.TimeSpan{ID: d.TimeSpan.ID}).
Find(new(model.TimeSpan)).
RecordNotFound()
assert.True(d.t, exist == existActual)
return d
}
// Tag adds a tag to the time span.
func (d *TimeSpanWithDatabase) Tag(key string, stringValue string) *TimeSpanWithDatabase {
tag := model.TimeSpanTag{
TimeSpanID: d.TimeSpan.ID,
Key: key,
StringValue: stringValue,
}
d.TimeSpan.Tags = append(d.TimeSpan.Tags, tag)
d.DB.Save(tag)
return d
}
// DashboardWithDatabase wraps gorm.DB and provides helper methods
type DashboardWithDatabase struct {
User model.User
Dashboard model.Dashboard
t assert.TestingT
*gorm.DB
}

103
test/db_test.go Normal file
View File

@@ -0,0 +1,103 @@
package test_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/traggo/server/model"
"github.com/traggo/server/test"
)
func TestInMemoryDB(t *testing.T) {
db := test.InMemoryDB(t)
defer db.Close()
assert.NotNil(t, db)
}
func TestDatabase(t *testing.T) {
db := test.InMemoryDB(t)
defer db.Close()
user := db.User(1)
user.AssertHasDevice("test device", false)
user.NewDevice(1, "lol", "test device")
user.AssertHasDevice("test device", true)
ts := user.TimeSpan("2009-06-30T18:30:00Z", "2009-06-30T18:40:00Z")
ts.AssertHasTagIgnoreValue("abc", false)
ts.AssertHasTag("abc", "def", false)
ts.Tag("abc", "def")
ts.AssertHasTag("abc", "def", true)
ts.AssertHasTagIgnoreValue("abc", true)
db.NewUser(2, "abc", true)
db.NewUserPass(3, "xxx", []byte{5, 5}, true)
expectedUsers := []model.User{
{ID: 1, Name: "test1", Pass: []uint8{1, 2, 3}, Admin: false},
{ID: 2, Name: "abc", Pass: []uint8{1, 2, 3}, Admin: true},
{ID: 3, Name: "xxx", Pass: []uint8{5, 5}, Admin: true}}
var users []model.User
db.Find(&users)
assert.Equal(t, expectedUsers, users)
expectedDevices := []model.Device{{
ID: 1,
Token: "lol",
Name: "test device",
UserID: 1,
Type: model.TypeNoExpiry,
ActiveAt: test.Time("2009-06-30T18:30:00Z"),
CreatedAt: test.Time("2009-06-30T18:30:00Z")}}
var devices []model.Device
db.Find(&devices)
assert.Equal(t, expectedDevices, devices)
expectedTimeSpans := []model.TimeSpan{{
ID: 1,
UserID: 1,
StartUserTime: test.Time("2009-06-30T18:30:00Z"),
StartUTC: test.Time("2009-06-30T18:30:00Z"),
EndUserTime: test.TimeP("2009-06-30T18:40:00Z"),
EndUTC: test.TimeP("2009-06-30T18:40:00Z"),
Tags: []model.TimeSpanTag{{
Key: "abc",
StringValue: "def",
TimeSpanID: 1,
}},
}}
var timeSpans []model.TimeSpan
db.Preload("Tags").Find(&timeSpans)
assert.Equal(t, expectedTimeSpans, timeSpans)
ts.AssertExists(true)
db.Delete(new(model.TimeSpan), "id = ?", ts.TimeSpan.ID)
ts.AssertExists(false)
user.AssertHasTagDefinition("oops", false)
user.NewTagDefinition("oops")
user.AssertHasTagDefinition("oops", true)
dash := user.Dashboard("hello")
dash.AssertHasRange("hello", false)
dash.Range("hello")
dash.AssertHasRange("hello", true)
dash.AssertHasEntry("abc", false)
dash.Entry("abc")
dash.AssertHasEntry("abc", true)
dash.AssertExists(true)
db.Delete(new(model.Dashboard), "id = ?", dash.Dashboard.ID)
dash.AssertExists(false)
user.AssertExists(true)
db.Delete(new(model.User), "id = ?", user.User.ID)
user.AssertExists(false)
}

13
test/fake/device.go Normal file
View File

@@ -0,0 +1,13 @@
package fake
import (
"context"
"github.com/traggo/server/auth"
"github.com/traggo/server/model"
)
// Device creates a context with a fake device.
func Device(device *model.Device) context.Context {
return auth.WithDevice(context.Background(), device)
}

22
test/fake/user.go Normal file
View File

@@ -0,0 +1,22 @@
package fake
import (
"context"
"github.com/traggo/server/auth"
"github.com/traggo/server/model"
)
// User create a context with a fake user.
func User(id int) context.Context {
return UserWithPerm(id, true)
}
// UserWithPerm create a context with a fake user.
func UserWithPerm(id int, admin bool) context.Context {
return auth.WithUser(context.Background(), &model.User{
ID: id,
Name: "fake",
Admin: admin,
})
}

9
test/fake_test.go Normal file
View File

@@ -0,0 +1,9 @@
package test_test
type fakeTesting struct {
hasErrors bool
}
func (t *fakeTesting) Errorf(format string, args ...interface{}) {
t.hasErrors = true
}

10
test/init.go Normal file
View File

@@ -0,0 +1,10 @@
package test
import (
"github.com/rs/zerolog"
"github.com/traggo/server/logger"
)
func init() {
logger.Init(zerolog.WarnLevel)
}

60
test/logger.go Normal file
View File

@@ -0,0 +1,60 @@
package test
import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/assert"
"github.com/traggo/server/logger"
)
// Logger the test logger with util methods
type Logger struct {
old zerolog.Logger
entries []Entry
t assert.TestingT
}
// Entry a test logging entry
type Entry struct {
Level zerolog.Level
Message string
}
// Dispose resets the logger
func (l *Logger) Dispose() {
log.Logger = l.old
}
// NewLogger creates a new test logger
func NewLogger(t assert.TestingT) *Logger {
logger := &Logger{t: t}
log.Logger = zerolog.New(&noop{}).With().Timestamp().Logger().Hook(logger)
return logger
}
// Run records log entries
func (l *Logger) Run(e *zerolog.Event, level zerolog.Level, message string) {
l.entries = append(l.entries, Entry{Level: level, Message: message})
}
// AssertCount asserts the amount of recorded logging entries
func (l *Logger) AssertCount(count int) {
assert.Len(l.t, l.entries, count)
}
// AssertEntryExists asserts that a logging entry exists
func (l *Logger) AssertEntryExists(entry Entry) {
assert.Contains(l.t, l.entries, entry)
}
type noop struct {
}
func (*noop) Write(p []byte) (n int, err error) {
return len(p), nil
}
// LogDebug enables debug log
func LogDebug() {
logger.Init(zerolog.DebugLevel)
}

63
test/logger_test.go Normal file
View File

@@ -0,0 +1,63 @@
package test_test
import (
"testing"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/assert"
"github.com/traggo/server/test"
)
func TestLogger_AssertCount_Succeeds(t *testing.T) {
logger := test.NewLogger(t)
defer logger.Dispose()
log.Error().Msg("error")
logger.AssertCount(1)
}
func TestLogger_AssertEntry_Succeeds(t *testing.T) {
logger := test.NewLogger(t)
defer logger.Dispose()
log.Error().Msg("error")
logger.AssertEntryExists(test.Entry{Message: "error", Level: zerolog.ErrorLevel})
}
func TestLogger_AssertCount_Fails(t *testing.T) {
fake := &fakeTesting{}
logger := test.NewLogger(fake)
defer logger.Dispose()
log.Error().Msg("error")
logger.AssertCount(2)
assert.True(t, fake.hasErrors)
}
func TestLogger_AssertEntry_Fails_wrongLevel(t *testing.T) {
fake := &fakeTesting{}
logger := test.NewLogger(fake)
defer logger.Dispose()
log.Error().Msg("error")
logger.AssertEntryExists(test.Entry{Message: "error", Level: zerolog.InfoLevel})
assert.True(t, fake.hasErrors)
}
func TestLogger_AssertEntry_Fails_wrongMessage(t *testing.T) {
fake := &fakeTesting{}
logger := test.NewLogger(fake)
defer logger.Dispose()
log.Error().Msg("error")
logger.AssertEntryExists(test.Entry{Message: "info", Level: zerolog.ErrorLevel})
assert.True(t, fake.hasErrors)
}

51
test/time.go Normal file
View File

@@ -0,0 +1,51 @@
package test
import (
"time"
"github.com/traggo/server/model"
)
// Time parses a time panics if not valid
func Time(value string) time.Time {
parse, firstErr := time.ParseInLocation(time.RFC3339, value, time.UTC)
if firstErr != nil {
var err error
parse, err = time.ParseInLocation(time.RFC3339Nano, value, time.UTC)
if err != nil {
panic(firstErr)
}
}
return parse
}
func timeWithCustomTZ(value string) time.Time {
parse, err := time.Parse(time.RFC3339, value)
if err != nil {
panic(err)
}
_, offset := parse.Zone()
return parse.In(time.FixedZone("unknown", offset))
}
// TimeP parses a time panics if not valid
func TimeP(value string) *time.Time {
t := Time(value)
return &t
}
// ModelTimeUTC parses a model.Time in utc time. panics if not valid
func ModelTimeUTC(value string) model.Time {
return model.Time(Time(value))
}
// ModelTime parses a model.Time panics if not valid
func ModelTime(value string) model.Time {
return model.Time(timeWithCustomTZ(value))
}
// ModelTimeP parses a model.Time panics if not valid
func ModelTimeP(value string) *model.Time {
modelTime := ModelTime(value)
return &modelTime
}

51
test/time_test.go Normal file
View File

@@ -0,0 +1,51 @@
package test
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestTime_panics(t *testing.T) {
assert.Panics(t, func() {
Time("asds")
})
}
func TestTime_succeeds(t *testing.T) {
Time("2009-06-30T18:30:00+02:00")
}
func TestModelTime_panics(t *testing.T) {
assert.Panics(t, func() {
ModelTime("asds")
})
}
func TestModelTime_succeeds(t *testing.T) {
ModelTime("2009-06-30T18:30:00+02:00")
}
func TestTimeP_panics(t *testing.T) {
assert.Panics(t, func() {
TimeP("asds")
})
}
func TestTimeP_succeeds(t *testing.T) {
TimeP("2009-06-30T18:30:00+02:00")
}
func TestModelTimeP_panics(t *testing.T) {
assert.Panics(t, func() {
ModelTimeP("asds")
})
}
func TestModelTimeP_succeeds(t *testing.T) {
ModelTimeP("2009-06-30T18:30:00+02:00")
}
func TestModelTimeUTC_succeeds(t *testing.T) {
ModelTimeUTC("2009-06-30T18:30:00+02:00")
}