Files
simply_based/spec/couch_attachment_spec.rb
GregandClaude Opus 4.8 86299b8827 SimplyBased 0.1.0 — Couchbase-native ActiveModel ORM
A from-scratch fork of simply_couch's model layer that runs on Couchbase
natively: `include SimplyBased::Model` instead of `include SimplyCouch::Model`.

- SimplyBased::Store replaces the CouchDB layer: KV documents, N1QL/SQL++
  finders + lazily-created GSIs, CAS mapped onto _rev. No design documents or
  JavaScript map/reduce — the hand-written association views become N1QL
  (belongs_to/has_many/has_one scalar FK; habtm via ANY ... SATISFIES + array
  index, or KV multi-get on the storing side).
- Redis replacements on the same cluster: Counter (atomic increment/decrement),
  Cache (ActiveSupport::Cache::Store over KV+TTL, drop-in Rails.cache/ActiveJob),
  Lock (insert-if-absent + TTL + CAS-checked release).
- Runs the simply_couch test suite against live Couchbase: 378/432 applicable
  examples green (CouchDB-mechanic specs tagged :couchdb_only and excluded).
- Remaining: ancestry (tree) + embedded_in reverse-lookups (recursive/embedded
  N1QL) and a few pagination/lifecycle edges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 13:58:47 -05:00

113 lines
3.2 KiB
Ruby

require 'spec_helper'
class CouchAttachmentDoc
include SimplyBased::Model
property :title
has_couch_attached :receipt
end
describe 'CouchDB attachments', :couchdb_only do
describe 'low-level API' do
it 'puts and fetches an attachment' do
doc = CouchAttachmentDoc.create(title: 'test')
doc.put_couch_attachment('file.txt', 'hello world', content_type: 'text/plain')
expect(doc.fetch_couch_attachment('file.txt')).to eq 'hello world'
end
it 'lists attachment names' do
doc = CouchAttachmentDoc.create(title: 'test')
doc.put_couch_attachment('a.txt', 'a')
doc.put_couch_attachment('b.txt', 'b')
expect(doc.couch_attachment_names).to contain_exactly('a.txt', 'b.txt')
end
it 'deletes an attachment' do
doc = CouchAttachmentDoc.create(title: 'test')
doc.put_couch_attachment('file.txt', 'data')
doc.delete_couch_attachment('file.txt')
expect(doc.couch_attachment?('file.txt')).to be false
end
it 'returns nil for missing attachment' do
doc = CouchAttachmentDoc.create(title: 'test')
expect(doc.fetch_couch_attachment('missing.txt')).to be_nil
end
end
describe 'has_couch_attached macro' do
it 'defines getter and setter' do
doc = CouchAttachmentDoc.new
expect(doc).to respond_to(:receipt)
expect(doc).to respond_to(:receipt=)
end
it 'defines URL helper' do
doc = CouchAttachmentDoc.new
expect(doc).to respond_to(:receipt_url)
end
it 'defines metadata properties' do
doc = CouchAttachmentDoc.new
expect(doc).to respond_to(:receipt_content_type)
expect(doc).to respond_to(:receipt_content_type=)
expect(doc).to respond_to(:receipt_size)
expect(doc).to respond_to(:receipt_size=)
end
it 'stores and retrieves data through the setter/getter' do
doc = CouchAttachmentDoc.create(title: 'test')
doc.receipt = 'binary data here'
doc.save
reloaded = CouchAttachmentDoc.find(doc.id)
expect(reloaded.receipt).to eq 'binary data here'
end
it 'tracks content type' do
doc = CouchAttachmentDoc.create(title: 'test')
doc.receipt = 'data'
doc.save
expect(doc.receipt_content_type).to eq 'binary/octet-stream'
end
it 'tracks size' do
doc = CouchAttachmentDoc.create(title: 'test')
doc.receipt = '12345'
doc.save
expect(doc.receipt_size).to eq 5
end
it 'handles non-persisted records via pending queue' do
doc = CouchAttachmentDoc.new(title: 'draft')
doc.receipt = 'pending data'
result = doc.save
expect(result).to be_truthy
reloaded = CouchAttachmentDoc.find(doc.id)
expect(reloaded.receipt).to eq 'pending data'
end
it 'updates attachment on second assignment' do
doc = CouchAttachmentDoc.create(title: 'test')
doc.receipt = 'v1'
doc.save
doc.receipt = 'v2'
doc.save
reloaded = CouchAttachmentDoc.find(doc.id)
expect(reloaded.receipt).to eq 'v2'
end
it 'generates a URL' do
doc = CouchAttachmentDoc.create(title: 'test')
doc.receipt = 'data'
doc.save
url = doc.receipt_url
expect(url).to include(doc._id)
expect(url).to include('receipt')
end
end
end