From 0af31bb0d95ceaca93118ef064fcc8989ab503e6 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Sun, 26 Mar 2023 13:29:33 +0200 Subject: [PATCH] more tests and db fixes --- README.md | 2 +- api/cleaner.go | 2 +- api/db.go | 9 +- api/db_test.go | 176 ++++++++++++++++++++++++++++++++++++++- api/upload_handlers.go | 4 +- common/types.go | 14 ++-- go.mod | 2 + go.sum | 2 + upctl/lib/client_test.go | 14 ++-- upctl/lib/output.go | 8 +- 10 files changed, 205 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 120e5bc..0f2b1c1 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ Upload: | expire | string | when the upload has to expire, either "asap" or a Duration using numbers and the letters d,h,m,s (days,hours,minutes,seconds), e.g. 2d4h30m | | file | string | filename after uploading, this is what a consumer gets when downloading it | | members | array of strings | list of the original filenames | -| uploaded | timestamp | time of object creation | +| created | timestamp | time of object creation | | context | string | the API context the upload has been created under | | url | string | the download URL | diff --git a/api/cleaner.go b/api/cleaner.go index f69b779..650bf9e 100644 --- a/api/cleaner.go +++ b/api/cleaner.go @@ -42,7 +42,7 @@ func DeleteExpiredUploads(conf *cfg.Config, db *Db) error { return fmt.Errorf("unable to unmarshal json: %s", err) } - if IsExpired(conf, upload.Uploaded.Time, upload.Expire) { + if IsExpired(conf, upload.Created.Time, upload.Expire) { if err := bucket.Delete([]byte(id)); err != nil { return nil } diff --git a/api/db.go b/api/db.go index 13ec82b..de8c9aa 100644 --- a/api/db.go +++ b/api/db.go @@ -102,7 +102,7 @@ func (db *Db) Delete(apicontext string, id string) error { return err } -func (db *Db) UploadsList(apicontext string, filter string, t int) (*common.Response, error) { +func (db *Db) List(apicontext string, filter string, t int) (*common.Response, error) { response := &common.Response{} err := db.bolt.View(func(tx *bolt.Tx) error { @@ -119,12 +119,12 @@ func (db *Db) UploadsList(apicontext string, filter string, t int) (*common.Resp var entryContext string if t == common.TypeUpload { - entryContext = entry.(common.Upload).Context + entryContext = entry.(*common.Upload).Context } else { - entryContext = entry.(common.Form).Context + entryContext = entry.(*common.Form).Context } - fmt.Printf("apicontext: %s, filter: %s\n", apicontext, filter) + //fmt.Printf("apicontext: %s, filter: %s\n", apicontext, filter) if apicontext != "" && db.cfg.Super != apicontext { // only return the uploads for this context if apicontext == entryContext { @@ -150,6 +150,7 @@ func (db *Db) UploadsList(apicontext string, filter string, t int) (*common.Resp } // we only return one obj here, but could return more later +// FIXME: turn the id into a filter and call (Uploads|Forms)List(), same code! func (db *Db) Get(apicontext string, id string, t int) (*common.Response, error) { response := &common.Response{} diff --git a/api/db_test.go b/api/db_test.go index b5083d0..8ecf90f 100644 --- a/api/db_test.go +++ b/api/db_test.go @@ -18,9 +18,13 @@ along with this program. If not, see . package api import ( + //"github.com/alecthomas/repr" + "github.com/maxatome/go-testdeep/td" "github.com/tlinden/cenophane/cfg" + "github.com/tlinden/cenophane/common" "os" "testing" + "time" ) func finalize(db *Db) { @@ -35,7 +39,7 @@ func finalize(db *Db) { func TestNew(t *testing.T) { var tests = []struct { name string - file string + dbfile string wantfail bool }{ {"opennew", "test.db", false}, @@ -43,7 +47,7 @@ func TestNew(t *testing.T) { } for _, tt := range tests { - c := &cfg.Config{DbFile: tt.file} + c := &cfg.Config{DbFile: tt.dbfile} t.Run(tt.name, func(t *testing.T) { db, err := NewDb(c) defer finalize(db) @@ -57,3 +61,171 @@ func TestNew(t *testing.T) { }) } } + +const timeformat string = "2006-01-02T15:04:05.000Z" + +var dbtests = []struct { + name string + dbfile string + wantfail bool + id string + context string + ts string + filter string + upload common.Upload + form common.Form +}{ + { + "upload", "test.db", false, "1", "foo", + "2023-03-10T11:45:00.000Z", "", + common.Upload{ + Id: "1", Expire: "asap", File: "none", Context: "foo", + Created: common.Timestamp{}}, + common.Form{}, + }, + { + "form", "test.db", false, "2", "foo", + "2023-03-10T11:45:00.000Z", "", + common.Upload{}, + common.Form{ + Id: "1", Expire: "asap", Description: "none", Context: "foo", + Created: common.Timestamp{}}, + }, +} + +/* + We need to test the whole Db operation in one run, because it + doesn't work well if using a global Db. +*/ +func TestDboperation(t *testing.T) { + for _, tt := range dbtests { + c := &cfg.Config{DbFile: tt.dbfile} + t.Run(tt.name, func(t *testing.T) { + // create new bbolt db + db, err := NewDb(c) + defer finalize(db) + + if err != nil { + t.Errorf("Could not open new DB: " + err.Error()) + } + + if tt.upload.Id != "" { + // set ts + ts, err := time.Parse(timeformat, tt.ts) + tt.upload.Created = common.Timestamp{Time: ts} + + // create new upload db object + err = db.Insert(tt.id, tt.upload) + if err != nil { + t.Errorf("Could not insert new upload object: " + err.Error()) + } + + // fetch it + response, err := db.Get(tt.context, tt.id, common.TypeUpload) + if err != nil { + t.Errorf("Could not fetch upload object: " + err.Error()) + } + + // is it there? + if len(response.Uploads) != 1 { + t.Errorf("db.Get() did not return an upload obj") + } + + // compare times + if !tt.upload.Created.Time.Equal(response.Uploads[0].Created.Time) { + t.Errorf("Timestamps don't match!\ngot: %s\nexp: %s\n", + response.Uploads[0].Created, tt.upload.Created) + } + + // equal them artificially, because otherwise td will + // fail because of time.Time.wall+ext, or TZ is missing + response.Uploads[0].Created = tt.upload.Created + + // compare + td.Cmp(t, response.Uploads[0], &tt.upload, tt.name) + + // fetch list + response, err = db.List(tt.context, tt.filter, common.TypeUpload) + if err != nil { + t.Errorf("Could not fetch uploads list: " + err.Error()) + } + + // is it there? + if len(response.Uploads) != 1 { + t.Errorf("db.List() did not return upload obj[s]") + } + + // delete + err = db.Delete(tt.context, tt.id) + if err != nil { + t.Errorf("Could not delete upload obj: " + err.Error()) + } + + // fetch again, shall return empty + response, err = db.Get(tt.context, tt.id, common.TypeUpload) + if err == nil { + t.Errorf("Could fetch upload object again although we deleted it") + } + } + + if tt.form.Id != "" { + // set ts + ts, err := time.Parse(timeformat, tt.ts) + tt.form.Created = common.Timestamp{Time: ts} + + // create new form db object + err = db.Insert(tt.id, tt.form) + if err != nil { + t.Errorf("Could not insert new form object: " + err.Error()) + } + + // fetch it + response, err := db.Get(tt.context, tt.id, common.TypeForm) + if err != nil { + t.Errorf("Could not fetch form object: " + err.Error()) + } + + // is it there? + if len(response.Forms) != 1 { + t.Errorf("db.Get() did not return an form obj") + } + + // compare times + if !tt.form.Created.Time.Equal(response.Forms[0].Created.Time) { + t.Errorf("Timestamps don't match!\ngot: %s\nexp: %s\n", + response.Forms[0].Created, tt.form.Created) + } + + // equal them artificially, because otherwise td will + // fail because of time.Time.wall+ext, or TZ is missing + response.Forms[0].Created = tt.form.Created + + // compare + td.Cmp(t, response.Forms[0], &tt.form, tt.name) + + // fetch list + response, err = db.List(tt.context, tt.filter, common.TypeForm) + if err != nil { + t.Errorf("Could not fetch forms list: " + err.Error()) + } + + // is it there? + if len(response.Forms) != 1 { + t.Errorf("db.FormsList() did not return form obj[s]") + } + + // delete + err = db.Delete(tt.context, tt.id) + if err != nil { + t.Errorf("Could not delete form obj: " + err.Error()) + } + + // fetch again, shall return empty + response, err = db.Get(tt.context, tt.id, common.TypeForm) + if err == nil { + t.Errorf("Could fetch form object again although we deleted it") + } + } + }) + } +} diff --git a/api/upload_handlers.go b/api/upload_handlers.go index 730c5f0..dcb06bc 100644 --- a/api/upload_handlers.go +++ b/api/upload_handlers.go @@ -62,7 +62,7 @@ func UploadPost(c *fiber.Ctx, cfg *cfg.Config, db *Db) error { } // init upload obj - entry := &common.Upload{Id: id, Uploaded: common.Timestamp{Time: time.Now()}} + entry := &common.Upload{Id: id, Created: common.Timestamp{Time: time.Now()}} // retrieve the API Context name from the session apicontext, err := GetApicontext(c) @@ -233,7 +233,7 @@ func UploadsList(c *fiber.Ctx, cfg *cfg.Config, db *Db) error { } // get list - uploads, err := db.UploadsList(apicontext, filter, common.TypeUpload) + uploads, err := db.List(apicontext, filter, common.TypeUpload) if err != nil { return JsonStatus(c, fiber.StatusForbidden, "Unable to list uploads: "+err.Error()) diff --git a/common/types.go b/common/types.go index c6af512..66d67e2 100644 --- a/common/types.go +++ b/common/types.go @@ -36,13 +36,13 @@ type Dbentry interface { } type Upload struct { - Id string `json:"id"` - Expire string `json:"expire"` - File string `json:"file"` // final filename (visible to the downloader) - Members []string `json:"members"` // contains multiple files, so File is an archive - Uploaded Timestamp `json:"uploaded"` - Context string `json:"context"` - Url string `json:"url"` + Id string `json:"id"` + Expire string `json:"expire"` + File string `json:"file"` // final filename (visible to the downloader) + Members []string `json:"members"` // contains multiple files, so File is an archive + Created Timestamp `json:"uploaded"` + Context string `json:"context"` + Url string `json:"url"` } // this one is also used for marshalling to the client diff --git a/go.mod b/go.mod index 8899487..0f5be47 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( require ( github.com/andybalholm/brotli v1.0.4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/klauspost/compress v1.15.9 // indirect @@ -26,6 +27,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/maxatome/go-testdeep v1.13.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect diff --git a/go.sum b/go.sum index cd34ef9..c9cae71 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPn github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/maxatome/go-testdeep v1.13.0 h1:EBmRelH7MhMfPvA+0kXAeOeJUXn3mzul5NmvjLDcQZI= +github.com/maxatome/go-testdeep v1.13.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= diff --git a/upctl/lib/client_test.go b/upctl/lib/client_test.go index 20a30cb..6876d18 100644 --- a/upctl/lib/client_test.go +++ b/upctl/lib/client_test.go @@ -111,7 +111,7 @@ func TestUploadFiles(t *testing.T) { name: "upload-file", apikey: "token", wantfail: false, - route: "/uploads/", + route: "/uploads", sendcode: 200, sendjson: `{"success": true}`, files: []string{"../t/t1"}, // pwd is lib/ ! @@ -121,7 +121,7 @@ func TestUploadFiles(t *testing.T) { name: "upload-dir", apikey: "token", wantfail: false, - route: "/uploads/", + route: "/uploads", sendcode: 200, sendjson: `{"success": true}`, files: []string{"../t"}, // pwd is lib/ ! @@ -131,7 +131,7 @@ func TestUploadFiles(t *testing.T) { name: "upload-catch-nonexistent-file", apikey: "token", wantfail: true, - route: "/uploads/", + route: "/uploads", sendcode: 200, sendjson: `{"success": false}`, files: []string{"../t/none"}, @@ -141,7 +141,7 @@ func TestUploadFiles(t *testing.T) { name: "upload-catch-no-access", apikey: "token", wantfail: true, - route: "/uploads/", + route: "/uploads", sendcode: 403, sendjson: `{"success": false}`, files: []string{"../t/t1"}, @@ -151,7 +151,7 @@ func TestUploadFiles(t *testing.T) { name: "upload-check-output", apikey: "token", wantfail: false, - route: "/uploads/", + route: "/uploads", sendcode: 200, sendjson: `{"uploads":[ { @@ -266,7 +266,7 @@ func TestDescribe(t *testing.T) { sendjson: listing, files: []string{"cc2c965a"}, method: "GET", - expect: `Uploaded: 2023-03-21 12:06:54.890501888`, + expect: `Created: 2023-03-21 12:06:54.890501888`, }, { name: "describe-catch-empty-json", @@ -292,7 +292,7 @@ func TestDescribe(t *testing.T) { for _, unit := range tests { var w bytes.Buffer - unit.route += unit.files[0] + "/" + unit.route += unit.files[0] Intercept(unit) Check(t, unit, &w, Describe(&w, conf, unit.files)) } diff --git a/upctl/lib/output.go b/upctl/lib/output.go index 11d6984..5f06aee 100644 --- a/upctl/lib/output.go +++ b/upctl/lib/output.go @@ -77,11 +77,11 @@ func WriteExtended(w io.Writer, response *common.Response) { // we shall only have 1 element, however, if we ever support more, here we go for _, entry := range response.Uploads { - expire := prepareExpire(entry.Expire, entry.Uploaded) + expire := prepareExpire(entry.Expire, entry.Created) fmt.Fprintf(w, format, "Id", entry.Id) fmt.Fprintf(w, format, "Expire", expire) fmt.Fprintf(w, format, "Context", entry.Context) - fmt.Fprintf(w, format, "Uploaded", entry.Uploaded) + fmt.Fprintf(w, format, "Created", entry.Created) fmt.Fprintf(w, format, "Filename", entry.File) fmt.Fprintf(w, format, "Url", entry.Url) fmt.Fprintln(w) @@ -120,11 +120,11 @@ func UploadsRespondTable(w io.Writer, resp *req.Response) error { data := [][]string{} for _, entry := range response.Uploads { data = append(data, []string{ - entry.Id, entry.Expire, entry.Context, entry.Uploaded.Format("2006-01-02 15:04:05"), + entry.Id, entry.Expire, entry.Context, entry.Created.Format("2006-01-02 15:04:05"), }) } - WriteTable(w, []string{"ID", "EXPIRE", "CONTEXT", "UPLOADED"}, data) + WriteTable(w, []string{"ID", "EXPIRE", "CONTEXT", "CREATED"}, data) return nil }