Duration is derived from the range so timer sessions can be corrected after the fact. Co-authored-by: Cursor <cursoragent@cursor.com>
131 lines
4.3 KiB
Go
131 lines
4.3 KiB
Go
package server
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"task/internal/sqlite"
|
|
)
|
|
|
|
func TestUpdateTimeStartEnd(t *testing.T) {
|
|
db, err := sqlite.Open(filepath.Join(t.TempDir(), "t.db"), []byte("secret"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
if _, err := db.CreateUser("admin", "pass", true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
srv := New(db)
|
|
handler := srv.Handler()
|
|
|
|
loginBody, _ := json.Marshal(map[string]string{"username": "admin", "password": "pass"})
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/login", bytes.NewReader(loginBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("login %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
cookie := rec.Result().Cookies()[0]
|
|
|
|
createBody, _ := json.Marshal(map[string]string{"name": "Demo"})
|
|
req = httptest.NewRequest(http.MethodPost, "/api/v1/projects", bytes.NewReader(createBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.AddCookie(cookie)
|
|
rec = httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusCreated {
|
|
t.Fatalf("create project %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
var proj map[string]any
|
|
_ = json.Unmarshal(rec.Body.Bytes(), &proj)
|
|
pid, _ := proj["id"].(string)
|
|
|
|
ticketBody, _ := json.Marshal(map[string]string{"title": "Work"})
|
|
req = httptest.NewRequest(http.MethodPost, "/api/v1/projects/"+pid+"/tickets", bytes.NewReader(ticketBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.AddCookie(cookie)
|
|
rec = httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusCreated {
|
|
t.Fatalf("create ticket %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
var ticket map[string]any
|
|
_ = json.Unmarshal(rec.Body.Bytes(), &ticket)
|
|
tid, _ := ticket["id"].(string)
|
|
|
|
timeBody, _ := json.Marshal(map[string]any{
|
|
"ticketId": tid,
|
|
"durationNs": int64(30 * time.Minute),
|
|
"source": "manual",
|
|
})
|
|
req = httptest.NewRequest(http.MethodPost, "/api/v1/projects/"+pid+"/time", bytes.NewReader(timeBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.AddCookie(cookie)
|
|
rec = httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusCreated {
|
|
t.Fatalf("create time %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
var entry map[string]any
|
|
_ = json.Unmarshal(rec.Body.Bytes(), &entry)
|
|
eid, _ := entry["id"].(string)
|
|
if eid == "" {
|
|
t.Fatal("no entry id")
|
|
}
|
|
|
|
start := time.Date(2024, 6, 1, 9, 0, 0, 0, time.UTC)
|
|
end := start.Add(90 * time.Minute)
|
|
patchBody, _ := json.Marshal(map[string]any{
|
|
"start": start,
|
|
"end": end,
|
|
})
|
|
req = httptest.NewRequest(http.MethodPatch, "/api/v1/projects/"+pid+"/time/"+eid, bytes.NewReader(patchBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.AddCookie(cookie)
|
|
rec = httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("patch start/end %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
_ = json.Unmarshal(rec.Body.Bytes(), &entry)
|
|
dur, _ := entry["duration"].(float64)
|
|
if time.Duration(dur) != 90*time.Minute {
|
|
t.Fatalf("duration want 90m got %v", time.Duration(dur))
|
|
}
|
|
|
|
// duration-only still works
|
|
patchBody, _ = json.Marshal(map[string]any{"durationNs": int64(45 * time.Minute)})
|
|
req = httptest.NewRequest(http.MethodPatch, "/api/v1/projects/"+pid+"/time/"+eid, bytes.NewReader(patchBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.AddCookie(cookie)
|
|
rec = httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("patch duration %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
_ = json.Unmarshal(rec.Body.Bytes(), &entry)
|
|
dur, _ = entry["duration"].(float64)
|
|
if time.Duration(dur) != 45*time.Minute {
|
|
t.Fatalf("duration want 45m got %v", time.Duration(dur))
|
|
}
|
|
|
|
// invalid range
|
|
badEnd := start.Add(-time.Minute)
|
|
patchBody, _ = json.Marshal(map[string]any{"start": start, "end": badEnd})
|
|
req = httptest.NewRequest(http.MethodPatch, "/api/v1/projects/"+pid+"/time/"+eid, bytes.NewReader(patchBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.AddCookie(cookie)
|
|
rec = httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusBadRequest {
|
|
t.Fatalf("invalid range want 400 got %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|