blob: 8f5c691bce52d4cd6e293e43068f5675092b1a00 [file] [edit]
// Copyright 2026 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package sheets
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"firmweaver/shared/auth"
)
const (
googleSheetsBaseURL = "https://docs.google.com/spreadsheets/"
mockSpreadsheetID = "mock-test-spreadsheet-id-placeholder-12345"
)
func TestParseGoogleSheetsURL(t *testing.T) {
ctx := context.Background()
standardURL := googleSheetsBaseURL + "d/" + mockSpreadsheetID + "/edit#gid=0"
id, gid, err := ResolveSheetURL(ctx, standardURL)
if err != nil || id != mockSpreadsheetID || gid != "0" {
t.Errorf("ResolveSheetURL standard URL failed: id=%s, gid=%s, err=%v", id, gid, err)
}
multiAccountURL := googleSheetsBaseURL + "u/1/d/" + mockSpreadsheetID + "/edit?gid=12345"
id, gid, err = ResolveSheetURL(ctx, multiAccountURL)
if err != nil || id != mockSpreadsheetID || gid != "12345" {
t.Errorf("ResolveSheetURL multi-account URL failed: id=%s, gid=%s, err=%v", id, gid, err)
}
rawID := mockSpreadsheetID
id, _, err = ResolveSheetURL(ctx, rawID)
if err != nil || id != rawID {
t.Errorf("ResolveSheetURL raw ID failed: id=%s, err=%v", id, err)
}
if _, _, err := ResolveSheetURL(ctx, "invalid-url"); err == nil {
t.Errorf("ResolveSheetURL expected error for invalid URL")
}
}
func TestFetchSheetDataMock(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "/values/") {
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"values": [][]interface{}{
{"Header1", "Header2", "Header3"},
{"Val1", "Val2"}, // Ragged row with missing 3rd column
},
})
return
}
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"sheets": []map[string]interface{}{
{
"properties": map[string]interface{}{
"sheetId": 0,
"title": "Sheet1",
},
},
},
})
}))
defer server.Close()
t.Setenv(APIBaseEnv, server.URL)
t.Setenv(auth.ConfigDirEnv, t.TempDir())
_ = auth.SaveToken(&auth.Token{
AccessToken: "mock-token",
Expiry: time.Now().Add(time.Hour),
})
testURL := googleSheetsBaseURL + "d/" + mockSpreadsheetID + "/edit#gid=0"
grid, err := Fetch(context.Background(), testURL, "")
if err != nil {
t.Fatalf("Fetch() failed: %v", err)
}
if len(grid) != 2 || len(grid[1]) != 3 || grid[1][2] != "" {
t.Errorf("expected padded grid with 3 columns, got: %+v", grid)
}
}
func TestFetchSheetData_HTTPErrors(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Forbidden", http.StatusForbidden)
}))
defer server.Close()
t.Setenv(APIBaseEnv, server.URL)
t.Setenv(auth.ConfigDirEnv, t.TempDir())
_ = auth.SaveToken(&auth.Token{
AccessToken: "mock-token",
Expiry: time.Now().Add(time.Hour),
})
testURL := googleSheetsBaseURL + "d/" + mockSpreadsheetID + "/edit"
_, err := Fetch(context.Background(), testURL, "Tab1")
if err == nil || !strings.Contains(err.Error(), "permission denied (HTTP 403)") {
t.Errorf("expected permission denied error, got: %v", err)
}
}
func TestFetchSheetData_MetadataMissingProperties(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Tab metadata without a "properties" object.
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"sheets": []map[string]interface{}{{}},
})
}))
defer server.Close()
t.Setenv(APIBaseEnv, server.URL)
t.Setenv(auth.ConfigDirEnv, t.TempDir())
_ = auth.SaveToken(&auth.Token{
AccessToken: "mock-token",
Expiry: time.Now().Add(time.Hour),
})
testURL := googleSheetsBaseURL + "d/" + mockSpreadsheetID + "/edit"
if _, err := Fetch(context.Background(), testURL, ""); err == nil ||
!strings.Contains(err.Error(), "missing tab properties") {
t.Errorf("expected missing tab properties error, got: %v", err)
}
}