Files
simply_based/spec/instance_lifecycle_spec.rb
T
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

227 lines
6.8 KiB
Ruby

require 'spec_helper'
describe 'Instance lifecycle' do
context 'design documents' do
let(:db_url) { SimplyBased.database_url }
it 'deletes all' do
expect(SimplyBased::Model.delete_all_design_documents(db_url)).to eq 0
user = User.create
Post.create(user: user)
user.posts
expect(SimplyBased::Model.delete_all_design_documents(db_url)).to eq 1
end
it 'compacts all' do
expect(SimplyBased::Model.compact_all_design_documents(db_url)).to eq 0
user = User.create
Post.create(user: user)
user.posts
expect(SimplyBased::Model.compact_all_design_documents(db_url)).to eq 1
end
end
context 'when creating instances' do
it 'populates the attributes' do
user = User.create(title: 'Mr.', name: 'Host Master')
expect(user.title).to eq 'Mr.'
expect(user.name).to eq 'Host Master'
end
it 'saves the instance' do
user = User.create(title: 'Mr.')
expect(user).not_to be_new_record
end
context 'with a bang' do
it 'does not raise an exception when saving succeeded' do
expect { User.create!(title: 'Mr.') }.not_to raise_error
end
it 'saves the user' do
user = User.create!(title: 'Mr.')
expect(user).not_to be_new_record
end
it 'raises an error when the validations failed' do
expect { User.create!(title: nil) }.to raise_error(RuntimeError, /Validations failed/)
end
end
context 'with a block' do
it 'calls the block with the record' do
user = User.create do |u|
u.title = 'Mr.'
end
expect(user.title).to eq 'Mr.'
end
it 'saves the record' do
user = User.create do |u|
u.title = 'Mr.'
end
expect(user).not_to be_new_record
end
it 'assigns attributes via the hash' do
user = User.create(title: 'Mr.') do |u|
u.name = 'Host Master'
end
expect(user.title).to eq 'Mr.'
expect(user.name).to eq 'Host Master'
end
end
end
context 'when saving an instance' do
it 'saves the instance' do
user = User.new(title: 'Mr.')
expect(user).to be_new_record
user.save
expect(user).not_to be_new_record
end
context 'when using save!' do
it 'raises an exception when a validation is not fulfilled' do
user = User.new
expect { user.save! }.to raise_error(RuntimeError, /Validations failed/)
end
end
context 'when using save(false)' do
it 'does not run the validations' do
user = User.new
user.save(false)
expect(user).not_to be_new
expect(user).not_to be_dirty
end
end
end
context 'when destroying an instance' do
it 'removes the instance' do
user = User.create(title: 'Mr')
expect { user.destroy }.to change { User.find(:all).size }.by(-1)
end
it 'returns the frozen instance' do
user = User.create(title: 'Mr')
expect(user.destroy).to eq user
end
end
context 'when updating attributes' do
it 'merges in the updated attributes' do
user = User.create(title: 'Mr.')
user.update_attributes(title: 'Mrs.')
expect(user.title).to eq 'Mrs.'
end
it 'saves the instance' do
user = User.create(title: 'Mr.')
user.update_attributes(title: 'Mrs.')
expect(user).not_to be_dirty
end
end
context 'when counting' do
context 'when counting all' do
it 'returns the number of objects in the database' do
CountMe.create(title: 'Mr.')
CountMe.create(title: 'Mrs.')
expect(CountMe.find(:all).size).to eq 2
expect(CountMe.count).to eq 2
end
it 'only counts the correct class' do
CountMe.create(title: 'Mr.')
DontCountMe.create(title: 'Foo')
expect(CountMe.find(:all).size).to eq 1
expect(CountMe.count).to eq 1
end
end
context 'when counting by prefix' do
it 'returns the number of matching objects' do
CountMe.create(title: 'Mr.')
CountMe.create(title: 'Mrs.')
expect(CountMe.find_all_by_title('Mr.').size).to eq 1
expect(CountMe.count_by_title('Mr.')).to eq 1
end
it 'only counts the correct class' do
CountMe.create(title: 'Mr.')
DontCountMe.create(title: 'Mr.')
expect(CountMe.find_all_by_title('Mr.').size).to eq 1
expect(CountMe.count_by_title('Mr.')).to eq 1
end
end
end
context 'when reloading an instance' do
it 'reloads new attributes from the database' do
user = User.create(title: 'Mr.', name: 'Host Master')
user2 = User.find(user.id)
user2.update_attributes(title: 'Mrs.', name: 'Hostess Masteress')
user.reload
expect(user.title).to eq 'Mrs.'
expect(user.name).to eq 'Hostess Masteress'
end
it 'removes attributes that are no longer in the database' do
user = User.create(title: 'Mr.', name: 'Host Master')
expect(user.name).not_to be_nil
same_user_in_different_thread = User.find(user.id)
same_user_in_different_thread.name = nil
same_user_in_different_thread.save!
expect(user.reload.name).to be_nil
end
it 'also removes foreign key attributes that are no longer in the database' do
user = User.create(title: 'Mr.', name: 'Host Master')
post = Post.create(user: user)
expect(post.user_id).not_to be_nil
same_post_in_different_thread = Post.find(post.id)
same_post_in_different_thread.user = nil
same_post_in_different_thread.save!
expect(post.reload.user_id).to be_nil
end
it 'is not dirty after reloading' do
user = User.create(title: 'Mr.', name: 'Host Master')
user2 = User.find(user.id)
user2.update_attributes(title: 'Mrs.', name: 'Hostess Masteress')
user.reload
expect(user).not_to be_dirty
end
it 'ensures that association caches for has_many are cleared' do
user = User.create(title: 'Mr.', name: 'Host Master')
post = Post.create(user: user)
expect(user.posts.size).to eq 1
expect(user.instance_variable_get('@posts')).not_to be_nil
user.reload
expect(user.instance_variable_get('@posts')).to be_nil
expect(user.posts.first).not_to be_nil
end
it 'ensures that association caches for belongs_to are cleared' do
user = User.create(title: 'Mr.', name: 'Host Master')
post = Post.create(user: user)
post.user
expect(post.instance_variable_get('@user')).not_to be_nil
post.reload
expect(post.instance_variable_get('@user')).to be_nil
expect(post.user).not_to be_nil
end
it 'updates the revision' do
user = User.create(title: 'Mr.', name: 'Host Master')
user2 = User.find(user.id)
user2.update_attributes(title: 'Mrs.', name: 'Hostess Masteress')
user.reload
expect(user._rev).to eq user2._rev
end
end
end