Improve Immich-backed photos endpoints with robust mapping/error handling, full albums CRUD, reliable list pagination/sorting/filtering, and shared Nextcloud quota checks before upload.
201 lines
5.6 KiB
Go
201 lines
5.6 KiB
Go
package photos
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestGetAssetsSupportsWrappedItemsResponse(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/assets" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"items": []map[string]any{
|
|
{
|
|
"id": "a1",
|
|
"originalFileName": "a.jpg",
|
|
"fileCreatedAt": "2026-01-01T00:00:00Z",
|
|
"exifInfo": map[string]any{
|
|
"fileSizeInByte": int64(42),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(server.URL)
|
|
assets, err := client.GetAssets(context.Background(), "key", 1, 50)
|
|
if err != nil {
|
|
t.Fatalf("GetAssets: %v", err)
|
|
}
|
|
if len(assets) != 1 || assets[0].FileSize != 42 {
|
|
t.Fatalf("assets = %+v", assets)
|
|
}
|
|
}
|
|
|
|
func TestGetAllAssetsPaginatesUpstream(t *testing.T) {
|
|
call := 0
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
call++
|
|
page := r.URL.Query().Get("page")
|
|
if page == "1" {
|
|
batch := make([]Asset, assetsPageSize)
|
|
for i := range batch {
|
|
batch[i] = Asset{ID: fmt.Sprintf("full-%d", i)}
|
|
}
|
|
_ = json.NewEncoder(w).Encode(batch)
|
|
return
|
|
}
|
|
if page == "2" {
|
|
_ = json.NewEncoder(w).Encode([]Asset{
|
|
{ID: "tail-1"},
|
|
{ID: "tail-2"},
|
|
})
|
|
return
|
|
}
|
|
_ = json.NewEncoder(w).Encode([]Asset{})
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(server.URL)
|
|
assets, err := client.GetAllAssets(context.Background(), "key")
|
|
if err != nil {
|
|
t.Fatalf("GetAllAssets: %v", err)
|
|
}
|
|
if len(assets) != assetsPageSize+2 {
|
|
t.Fatalf("len = %d, want %d", len(assets), assetsPageSize+2)
|
|
}
|
|
if call != 2 {
|
|
t.Fatalf("upstream calls = %d, want 2", call)
|
|
}
|
|
}
|
|
|
|
func TestCreateAlbumMapsImmichPayload(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost || r.URL.Path != "/albums" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
if r.Header.Get("x-api-key") != "key" {
|
|
t.Fatalf("api key = %q", r.Header.Get("x-api-key"))
|
|
}
|
|
var payload createAlbumPayload
|
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
|
t.Fatalf("decode payload: %v", err)
|
|
}
|
|
if payload.AlbumName != "Trip" || payload.Description != "Summer" || len(payload.AssetIDs) != 1 || payload.AssetIDs[0] != "asset-1" {
|
|
t.Fatalf("payload = %+v", payload)
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
_ = json.NewEncoder(w).Encode(Album{ID: "album-1", Name: payload.AlbumName})
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(server.URL)
|
|
album, err := client.CreateAlbum(context.Background(), "key", "Trip", "Summer", []string{"asset-1"})
|
|
if err != nil {
|
|
t.Fatalf("CreateAlbum: %v", err)
|
|
}
|
|
if album.ID != "album-1" || album.Name != "Trip" {
|
|
t.Fatalf("album = %+v", album)
|
|
}
|
|
}
|
|
|
|
func TestUploadAssetStatusMapped(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(server.URL)
|
|
_, err := client.UploadAsset(context.Background(), "key", strings.NewReader("body"), "application/octet-stream")
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
var statusErr *HTTPStatusError
|
|
if !errors.As(err, &statusErr) || statusErr.StatusCode != http.StatusBadRequest {
|
|
t.Fatalf("err = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestGetAlbumNotFound(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.NotFound(w, r)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(server.URL)
|
|
_, err := client.GetAlbum(context.Background(), "key", "missing")
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
var statusErr *HTTPStatusError
|
|
if !errors.As(err, &statusErr) || statusErr.StatusCode != http.StatusNotFound {
|
|
t.Fatalf("err = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestAddAlbumAssetsSendsBulkIDs(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPut || r.URL.Path != "/albums/album-1/assets" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
var payload bulkIDsPayload
|
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
|
t.Fatalf("decode payload: %v", err)
|
|
}
|
|
if len(payload.IDs) != 2 || payload.IDs[0] != "a1" || payload.IDs[1] != "a2" {
|
|
t.Fatalf("payload = %+v", payload)
|
|
}
|
|
_ = json.NewEncoder(w).Encode([]BulkAssetResult{{ID: "a1", Success: true}, {ID: "a2", Success: true}})
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(server.URL)
|
|
results, err := client.AddAlbumAssets(context.Background(), "key", "album-1", []string{"a1", "a2"})
|
|
if err != nil {
|
|
t.Fatalf("AddAlbumAssets: %v", err)
|
|
}
|
|
if len(results) != 2 {
|
|
t.Fatalf("results = %+v", results)
|
|
}
|
|
}
|
|
|
|
func TestRemoveAlbumAssetsUsesDeleteWithBody(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodDelete || r.URL.Path != "/albums/album-1/assets" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
t.Fatalf("read body: %v", err)
|
|
}
|
|
if string(body) != `{"ids":["a1"]}` {
|
|
t.Fatalf("body = %s", string(body))
|
|
}
|
|
_ = json.NewEncoder(w).Encode([]BulkAssetResult{{ID: "a1", Success: true}})
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(server.URL)
|
|
results, err := client.RemoveAlbumAssets(context.Background(), "key", "album-1", []string{"a1"})
|
|
if err != nil {
|
|
t.Fatalf("RemoveAlbumAssets: %v", err)
|
|
}
|
|
if len(results) != 1 || !results[0].Success {
|
|
t.Fatalf("results = %+v", results)
|
|
}
|
|
}
|