Files
go-onlyoffice/client_test.go
T
eSlider 0e80a1a058 Add milestone create and task start dates so Gantt bars can exist.
CLI: oo projects milestone-create, oo tasks create --start --milestone.
2026-08-13 10:21:15 +01:00

239 lines
6.6 KiB
Go

//go:build integration
package onlyoffice
// Integration tests — hit a live OnlyOffice Workspace instance. Credentials
// come from ONLYOFFICE_URL / ONLYOFFICE_USER / ONLYOFFICE_PASS (aliases
// _HOST / _NAME / _PASSWORD also accepted). Without credentials every test
// here skips.
//
// Run with:
//
// go test -tags=integration ./...
//
// These tests are destructive on the target instance. They create projects
// with titles prefixed "go-onlyoffice-test-" and clean up afterwards. Do not
// run against an instance you don't own.
import (
"context"
"strconv"
"strings"
"testing"
"time"
)
func skipWithoutCredentials(t *testing.T) Credentials {
t.Helper()
c := GetEnvironmentCredentials()
if c.Url == "" || c.User == "" || c.Password == "" {
t.Skip("ONLYOFFICE_URL/USER/PASS not set — skipping integration test")
}
return c
}
func liveClient(t *testing.T) *Client {
t.Helper()
c := NewClient(skipWithoutCredentials(t))
c.SetDefaults(GetEnvironmentDefaults())
return c
}
const testProjectPrefix = "go-onlyoffice-test-"
func cleanupTestProjects(t *testing.T, c *Client) {
t.Helper()
projects, err := c.GetProjects()
if err != nil {
t.Logf("cleanup: GetProjects: %v", err)
return
}
for _, p := range projects {
if p.Title != nil && strings.HasPrefix(*p.Title, testProjectPrefix) {
if _, err := c.DeleteProject(*p.ID); err != nil {
t.Logf("cleanup: DeleteProject %d: %v", *p.ID, err)
}
}
}
}
func TestIntegrationAuthenticateContext(t *testing.T) {
c := liveClient(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := c.AuthenticateContext(ctx); err != nil {
t.Fatalf("AuthenticateContext: %v", err)
}
if c.token == nil || c.token.Value == "" {
t.Fatal("token not cached after AuthenticateContext")
}
// Second call must hit the cache.
cachedValue := c.token.Value
if err := c.AuthenticateContext(ctx); err != nil {
t.Fatalf("AuthenticateContext (cached): %v", err)
}
if c.token.Value != cachedValue {
t.Fatal("cached token was replaced unexpectedly")
}
// Invalidate forces re-auth.
c.InvalidateToken()
if c.token != nil {
t.Fatal("InvalidateToken did not clear the cache")
}
if err := c.AuthenticateContext(ctx); err != nil {
t.Fatalf("AuthenticateContext (after invalidate): %v", err)
}
if c.token == nil {
t.Fatal("post-invalidate auth left token nil")
}
}
func TestIntegrationGetProjectsAndSelf(t *testing.T) {
c := liveClient(t)
projects, err := c.GetProjects()
if err != nil {
t.Fatalf("GetProjects: %v", err)
}
if projects == nil {
t.Fatal("nil slice from GetProjects")
}
ctx := context.Background()
uid, err := c.SelfUserID(ctx)
if err != nil {
t.Fatalf("SelfUserID: %v", err)
}
if uid == "" {
t.Fatal("empty self user id")
}
}
func TestIntegrationProjectAndTaskLifecycle(t *testing.T) {
c := liveClient(t)
t.Cleanup(func() { cleanupTestProjects(t, c) })
suffix := time.Now().UTC().Format("20060102-150405")
title := testProjectPrefix + suffix
project, err := c.CreateProject(NewProjectRequest{
Title: title,
Description: "integration test from go-onlyoffice",
})
if err != nil {
t.Fatalf("CreateProject: %v", err)
}
if project.ID == nil {
t.Fatal("created project without id")
}
updated, err := c.UpdateProject(ProjectUpdateRequest{
ID: *project.ID,
Title: title + " (updated)",
Description: "updated",
ResponsibleID: *project.Responsible.ID,
})
if err != nil {
t.Fatalf("UpdateProject: %v", err)
}
if updated.Title == nil || *updated.Title != title+" (updated)" {
t.Errorf("title not updated: %+v", updated.Title)
}
start := Time(time.Now().AddDate(0, 0, -2))
deadline := Time(time.Now().AddDate(0, 0, 2))
ms, err := c.CreateMilestone(NewMilestoneRequest{
ProjectID: *project.ID,
Title: "integration milestone",
Deadline: deadline,
})
if err != nil {
t.Fatalf("CreateMilestone: %v", err)
}
if ms == nil || ms.ID == nil {
t.Fatal("created milestone without id")
}
t.Cleanup(func() {
if err := c.DeleteMilestone(*ms.ID); err != nil {
t.Logf("cleanup DeleteMilestone: %v", err)
}
})
task, err := c.CreateProjectTask(NewProjectTaskRequest{
ProjectId: *project.ID,
Title: "integration parent task",
Description: "from go-onlyoffice integration suite",
StartDate: start,
Deadline: deadline,
Priority: int(TaskPriorityNormal),
MilestoneId: int(*ms.ID),
})
if err != nil {
t.Fatalf("CreateProjectTask: %v", err)
}
if task.ID == nil {
t.Fatal("created task without id")
}
newStart := Time(time.Now().AddDate(0, 0, -14))
newDeadline := Time(time.Now().AddDate(0, 0, 3))
if _, err := c.UpdateProjectTask(ProjectTaskUpdateRequest{
ID: *task.ID,
Title: "integration parent task (updated)",
Description: "updated",
StartDate: &newStart,
Deadline: &newDeadline,
}); err != nil {
t.Fatalf("UpdateProjectTask: %v", err)
}
// Subtask uses the form-encoded helper; exercises httpx.postForm path.
ctx := context.Background()
sub, err := c.AddSubtask(ctx, strconv.Itoa(*task.ID), "integration subtask")
if err != nil {
t.Fatalf("AddSubtask: %v", err)
}
if _, ok := sub["id"]; !ok {
t.Errorf("subtask response missing id: %+v", sub)
}
}
func TestIntegrationCalendarAndCRMRead(t *testing.T) {
c := liveClient(t)
ctx := context.Background()
start := time.Now().Format("2006-01-02")
end := time.Now().AddDate(0, 0, 14).Format("2006-01-02")
if _, err := c.ListCalendars(ctx, start, end); err != nil {
t.Errorf("ListCalendars: %v", err)
}
if _, err := c.ListEvents(ctx, start, end); err != nil {
t.Errorf("ListEvents: %v", err)
}
if _, _, err := c.ListContacts(ctx, 5, 0, ""); err != nil {
t.Errorf("ListContacts: %v", err)
}
if _, _, err := c.ListOpportunities(ctx, 5, 0); err != nil {
t.Errorf("ListOpportunities: %v", err)
}
if _, err := c.ListDealStages(ctx); err != nil {
t.Errorf("ListDealStages: %v", err)
}
}
// TestIntegrationDryRunList covers the tiniest project-tasks read that
// inventar-sync depends on; confirms GetTasks deserialises real responses.
func TestIntegrationListTasks(t *testing.T) {
c := liveClient(t)
defaults := GetEnvironmentDefaults()
// ProjectID default is "33"; skip the read-only list if the caller hasn't
// pointed us at a valid project (we don't know which projects exist).
if defaults.ProjectID == "" {
t.Skip("ONLYOFFICE_PROJECT_ID not configured")
}
pid, err := strconv.Atoi(defaults.ProjectID)
if err != nil || pid <= 0 {
t.Skipf("ONLYOFFICE_PROJECT_ID %q is not a positive int", defaults.ProjectID)
}
if _, err := c.GetTasks(NewProjectGetTasksRequest(pid)); err != nil {
t.Fatalf("GetTasks(%d): %v", pid, err)
}
}