267 lines
7.4 KiB
Go
267 lines
7.4 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"google.golang.org/api/drive/v3"
|
|
"google.golang.org/api/option"
|
|
)
|
|
|
|
func TestExecute_SheetsExport_DefaultXLSX(t *testing.T) {
|
|
origNew := newDriveService
|
|
origExport := driveExportDownload
|
|
t.Cleanup(func() {
|
|
newDriveService = origNew
|
|
driveExportDownload = origExport
|
|
})
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"id": "sheet1",
|
|
"name": "My Sheet",
|
|
"mimeType": "application/vnd.google-apps.spreadsheet",
|
|
})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
svc, err := drive.NewService(context.Background(),
|
|
option.WithoutAuthentication(),
|
|
option.WithHTTPClient(srv.Client()),
|
|
option.WithEndpoint(srv.URL+"/"),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("NewService: %v", err)
|
|
}
|
|
newDriveService = func(context.Context, string) (*drive.Service, error) { return svc, nil }
|
|
|
|
var gotMime string
|
|
driveExportDownload = func(_ context.Context, _ *drive.Service, fileID string, mimeType string) (*http.Response, error) {
|
|
gotMime = mimeType
|
|
return &http.Response{
|
|
Status: "200 OK",
|
|
StatusCode: http.StatusOK,
|
|
Body: io.NopCloser(strings.NewReader("XLSX-FAKE")),
|
|
}, nil
|
|
}
|
|
|
|
dest := filepath.Join(t.TempDir(), "out")
|
|
out := captureStdout(t, func() {
|
|
_ = captureStderr(t, func() {
|
|
if err := Execute([]string{"--json", "--account", "a@b.com", "sheets", "export", "sheet1", "--out", dest}); err != nil {
|
|
t.Fatalf("Execute: %v", err)
|
|
}
|
|
})
|
|
})
|
|
|
|
if gotMime != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" {
|
|
t.Fatalf("mimeType=%q", gotMime)
|
|
}
|
|
|
|
var parsed struct {
|
|
Path string `json:"path"`
|
|
Size int64 `json:"size"`
|
|
}
|
|
if err := json.Unmarshal([]byte(out), &parsed); err != nil {
|
|
t.Fatalf("json parse: %v\nout=%q", err, out)
|
|
}
|
|
if !strings.HasSuffix(parsed.Path, ".xlsx") {
|
|
t.Fatalf("expected .xlsx path, got %q", parsed.Path)
|
|
}
|
|
if b, err := os.ReadFile(parsed.Path); err != nil || string(b) != "XLSX-FAKE" {
|
|
t.Fatalf("file mismatch: err=%v body=%q", err, string(b))
|
|
}
|
|
}
|
|
|
|
func TestExecute_DocsExport_DOCX(t *testing.T) {
|
|
origNew := newDriveService
|
|
origExport := driveExportDownload
|
|
t.Cleanup(func() {
|
|
newDriveService = origNew
|
|
driveExportDownload = origExport
|
|
})
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"id": "doc1",
|
|
"name": "My Doc",
|
|
"mimeType": "application/vnd.google-apps.document",
|
|
})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
svc, err := drive.NewService(context.Background(),
|
|
option.WithoutAuthentication(),
|
|
option.WithHTTPClient(srv.Client()),
|
|
option.WithEndpoint(srv.URL+"/"),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("NewService: %v", err)
|
|
}
|
|
newDriveService = func(context.Context, string) (*drive.Service, error) { return svc, nil }
|
|
|
|
var gotMime string
|
|
driveExportDownload = func(_ context.Context, _ *drive.Service, fileID string, mimeType string) (*http.Response, error) {
|
|
gotMime = mimeType
|
|
return &http.Response{
|
|
Status: "200 OK",
|
|
StatusCode: http.StatusOK,
|
|
Body: io.NopCloser(strings.NewReader("DOCX-FAKE")),
|
|
}, nil
|
|
}
|
|
|
|
dest := filepath.Join(t.TempDir(), "out")
|
|
out := captureStdout(t, func() {
|
|
_ = captureStderr(t, func() {
|
|
if err := Execute([]string{"--json", "--account", "a@b.com", "docs", "export", "doc1", "--format", "docx", "--out", dest}); err != nil {
|
|
t.Fatalf("Execute: %v", err)
|
|
}
|
|
})
|
|
})
|
|
|
|
if gotMime != "application/vnd.openxmlformats-officedocument.wordprocessingml.document" {
|
|
t.Fatalf("mimeType=%q", gotMime)
|
|
}
|
|
|
|
var parsed struct {
|
|
Path string `json:"path"`
|
|
}
|
|
if err := json.Unmarshal([]byte(out), &parsed); err != nil {
|
|
t.Fatalf("json parse: %v\nout=%q", err, out)
|
|
}
|
|
if !strings.HasSuffix(parsed.Path, ".docx") {
|
|
t.Fatalf("expected .docx path, got %q", parsed.Path)
|
|
}
|
|
}
|
|
|
|
func TestExecute_SlidesExport_DefaultPPTX(t *testing.T) {
|
|
origNew := newDriveService
|
|
origExport := driveExportDownload
|
|
t.Cleanup(func() {
|
|
newDriveService = origNew
|
|
driveExportDownload = origExport
|
|
})
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"id": "slides1",
|
|
"name": "My Slides",
|
|
"mimeType": "application/vnd.google-apps.presentation",
|
|
})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
svc, err := drive.NewService(context.Background(),
|
|
option.WithoutAuthentication(),
|
|
option.WithHTTPClient(srv.Client()),
|
|
option.WithEndpoint(srv.URL+"/"),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("NewService: %v", err)
|
|
}
|
|
newDriveService = func(context.Context, string) (*drive.Service, error) { return svc, nil }
|
|
|
|
var gotMime string
|
|
driveExportDownload = func(_ context.Context, _ *drive.Service, fileID string, mimeType string) (*http.Response, error) {
|
|
gotMime = mimeType
|
|
return &http.Response{
|
|
Status: "200 OK",
|
|
StatusCode: http.StatusOK,
|
|
Body: io.NopCloser(strings.NewReader("PPTX-FAKE")),
|
|
}, nil
|
|
}
|
|
|
|
dest := filepath.Join(t.TempDir(), "out")
|
|
out := captureStdout(t, func() {
|
|
_ = captureStderr(t, func() {
|
|
if err := Execute([]string{"--json", "--account", "a@b.com", "slides", "export", "slides1", "--out", dest}); err != nil {
|
|
t.Fatalf("Execute: %v", err)
|
|
}
|
|
})
|
|
})
|
|
|
|
if gotMime != "application/vnd.openxmlformats-officedocument.presentationml.presentation" {
|
|
t.Fatalf("mimeType=%q", gotMime)
|
|
}
|
|
|
|
var parsed struct {
|
|
Path string `json:"path"`
|
|
}
|
|
if err := json.Unmarshal([]byte(out), &parsed); err != nil {
|
|
t.Fatalf("json parse: %v\nout=%q", err, out)
|
|
}
|
|
if !strings.HasSuffix(parsed.Path, ".pptx") {
|
|
t.Fatalf("expected .pptx path, got %q", parsed.Path)
|
|
}
|
|
}
|
|
|
|
func TestExecute_DocsExport_RejectsNonDoc(t *testing.T) {
|
|
origNew := newDriveService
|
|
origExport := driveExportDownload
|
|
t.Cleanup(func() {
|
|
newDriveService = origNew
|
|
driveExportDownload = origExport
|
|
})
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"id": "x",
|
|
"name": "Not a Doc",
|
|
"mimeType": "application/vnd.google-apps.spreadsheet",
|
|
})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
svc, err := drive.NewService(context.Background(),
|
|
option.WithoutAuthentication(),
|
|
option.WithHTTPClient(srv.Client()),
|
|
option.WithEndpoint(srv.URL+"/"),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("NewService: %v", err)
|
|
}
|
|
newDriveService = func(context.Context, string) (*drive.Service, error) { return svc, nil }
|
|
|
|
called := false
|
|
driveExportDownload = func(context.Context, *drive.Service, string, string) (*http.Response, error) {
|
|
called = true
|
|
return nil, errors.New("unexpected export call")
|
|
}
|
|
|
|
err = Execute([]string{"--json", "--account", "a@b.com", "docs", "export", "x", "--out", filepath.Join(t.TempDir(), "out")})
|
|
if err == nil || !strings.Contains(err.Error(), "not a Google Doc") {
|
|
t.Fatalf("unexpected err=%v", err)
|
|
}
|
|
if called {
|
|
t.Fatalf("export should not be called")
|
|
}
|
|
}
|