rentease/internal/service/booking/sync_test.go
Ruidy 9b2510460a
Some checks are pending
CI / checks (push) Waiting to run
feat: store invoice PDFs in minio
2026-03-20 23:58:57 +01:00

271 lines
6.8 KiB
Go

package booking
import (
"errors"
"log/slog"
"testing"
"time"
"github.com/rjNemo/rentease/internal/config"
)
func TestParseFromAPI_CreatesItemsForBookingSync(t *testing.T) {
store := newStubStore()
parser := stubParser{
booking: Booking{
From: time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
To: time.Date(2025, time.January, 4, 0, 0, 0, 0, time.UTC),
Name: "Jane Doe",
PhoneNumber: "123456",
Email: "jane@example.com",
Platform: config.Platform("Booking"),
CustomerNumber: 2,
PlatformFees: 15.0,
Items: []Item{{
Item: " T3 ",
Quantity: 3,
Price: 80.0,
PaymentMethod: "Card",
}},
},
}
svc, err := NewService(slog.Default(), store, parser, noopPDF{}, nil, 0)
if err != nil {
t.Fatalf("unexpected error creating service: %v", err)
}
booking, err := svc.ParseFromAPI("raw booking content")
if err != nil {
t.Fatalf("unexpected error parsing booking: %v", err)
}
if len(store.items) != 2 {
t.Fatalf("expected 2 items to be created (booking item + taxes), got %d", len(store.items))
}
if store.items[0].Item != "T3" {
t.Fatalf("expected base item to be trimmed to host item name, got %q", store.items[0].Item)
}
if store.items[1].Item != "Taxes" {
t.Fatalf("expected taxes item to be created, got %q", store.items[1].Item)
}
if len(booking.Items) != 2 {
t.Fatalf("expected booking to include created items, got %d", len(booking.Items))
}
}
func TestParseFromAPI_DoesNotCreateTaxesForNonBookingPlatform(t *testing.T) {
store := newStubStore()
parser := stubParser{
booking: Booking{
From: time.Date(2025, time.March, 1, 0, 0, 0, 0, time.UTC),
To: time.Date(2025, time.March, 3, 0, 0, 0, 0, time.UTC),
Name: "Alex Smith",
PhoneNumber: "987654",
Email: "alex@example.com",
Platform: config.Platform("AirBnb"),
CustomerNumber: 2,
Items: []Item{{
Item: "t2",
Quantity: 2,
Price: 59.0,
PaymentMethod: "Card",
}},
},
}
svc, err := NewService(slog.Default(), store, parser, noopPDF{}, nil, 0)
if err != nil {
t.Fatalf("unexpected error creating service: %v", err)
}
if _, err := svc.ParseFromAPI("raw booking content"); err != nil {
t.Fatalf("unexpected error parsing booking: %v", err)
}
if len(store.items) != 1 {
t.Fatalf("expected only base item without taxes for non-Booking platform, got %d items", len(store.items))
}
if store.items[0].Item != "T2" {
t.Fatalf("expected base item to match host item name, got %q", store.items[0].Item)
}
}
func TestParseFromAPI_CreatesFallbackItemWhenUnknown(t *testing.T) {
store := newStubStore()
parser := stubParser{
booking: Booking{
From: time.Date(2025, time.February, 1, 0, 0, 0, 0, time.UTC),
To: time.Date(2025, time.February, 2, 0, 0, 0, 0, time.UTC),
Name: "Jane Doe",
PhoneNumber: "123456",
Email: "jane@example.com",
Platform: config.Platform("Booking"),
CustomerNumber: 1,
Items: []Item{{
Item: "New Suite",
Quantity: 1,
Price: 120.0,
PaymentMethod: "Card",
}},
},
}
svc, err := NewService(slog.Default(), store, parser, noopPDF{}, nil, 0)
if err != nil {
t.Fatalf("unexpected error creating service: %v", err)
}
if _, err := svc.ParseFromAPI("raw booking content"); err != nil {
t.Fatalf("unexpected error parsing booking: %v", err)
}
if len(store.items) != 1 {
t.Fatalf("expected fallback item to be created, got %d items", len(store.items))
}
if store.items[0].Item != "New Suite" {
t.Fatalf("expected fallback item name to match parsed value, got %q", store.items[0].Item)
}
}
func TestParseFromAPI_NormalizesVerboseItemNameToHostItem(t *testing.T) {
store := newStubStore()
parser := stubParser{
booking: Booking{
From: time.Date(2025, time.April, 1, 0, 0, 0, 0, time.UTC),
To: time.Date(2025, time.April, 3, 0, 0, 0, 0, time.UTC),
Name: "Chris P Bacon",
PhoneNumber: "999999",
Email: "chris@example.com",
Platform: config.Platform("Booking"),
CustomerNumber: 2,
Items: []Item{{
Item: "One-Bedroom House (T2 - VillaFleurie au bourg du Gosier)",
Quantity: 2,
Price: 59.0,
PaymentMethod: "Card",
}},
},
}
svc, err := NewService(slog.Default(), store, parser, noopPDF{}, nil, 0)
if err != nil {
t.Fatalf("unexpected error creating service: %v", err)
}
if _, err := svc.ParseFromAPI("raw booking content"); err != nil {
t.Fatalf("unexpected error parsing booking: %v", err)
}
if len(store.items) != 2 {
t.Fatalf("expected base item plus taxes, got %d items", len(store.items))
}
if store.items[0].Item != "T2" {
t.Fatalf("expected verbose item name to normalize to host item 'T2', got %q", store.items[0].Item)
}
}
type stubParser struct {
booking Booking
err error
}
func (p stubParser) Parse(rawContent string) (*Booking, error) {
if p.err != nil {
return nil, p.err
}
cp := p.booking
return &cp, nil
}
type stubStore struct {
bookings []*Booking
items []*Item
invoiceDocument *InvoiceDocument
upsertInvoiceErr error
}
func newStubStore() *stubStore {
return &stubStore{
bookings: make([]*Booking, 0),
items: make([]*Item, 0),
}
}
func (s *stubStore) All() []*Line {
return nil
}
func (s *stubStore) Search(value string) []*Line {
return nil
}
func (s *stubStore) List(from, to time.Time) ([]*Line, error) {
return nil, nil
}
func (s *stubStore) CardTotal(from, to time.Time) (float64, error) {
return 0, nil
}
func (s *stubStore) Get(id int) (*Booking, error) {
return nil, errors.New("not implemented")
}
func (s *stubStore) Create(b *Booking) error {
b.ID = len(s.bookings) + 1
s.bookings = append(s.bookings, b)
return nil
}
func (s *stubStore) Update(b *Booking) error {
return nil
}
func (s *stubStore) Cancel(id int) error {
return nil
}
func (s *stubStore) UpsertInvoiceDocument(doc *InvoiceDocument) error {
if s.upsertInvoiceErr != nil {
return s.upsertInvoiceErr
}
cp := *doc
s.invoiceDocument = &cp
return nil
}
func (s *stubStore) CreateItem(i *Item) error {
i.ID = len(s.items) + 1
s.items = append(s.items, i)
return nil
}
func (s *stubStore) PayItem(id int) (*Item, error) {
return nil, nil
}
func (s *stubStore) GetItem(id int) (*Item, error) {
return nil, nil
}
func (s *stubStore) UpdateItem(id int, item string, paymentMethod string, paymentStatus string, qty int, price float64) (*Item, error) {
return nil, nil
}
type noopPDF struct{}
func (noopPDF) BuildInvoice(invoice Invoice) (*GeneratedFile, error) {
return &GeneratedFile{Name: "invoice.pdf", ContentType: "application/pdf"}, nil
}
func (noopPDF) BuildReport(report ReportData, period string, month, year int) (string, error) {
return "", nil
}