Live-run finding: the full suite against Couchbase was 423 examples with 17 failures — all in embedding_spec. The session report claimed embedded specs were 'excluded by design', but the exclusion filter targeted a :embedding tag no example carries, so the file ran (and failed) all along. Three fixes: - HasManyEmbedded::Property#build wired parent_object before installing the collection ivar on the parent; parent_object= reaches back into the parent's collection getter, which re-entered build -> SystemStackError on any access to embedded documents loaded from the database. Install the ivar first. - EmbeddedClass.all / .count referenced raw JS views (all_documents / all_documents_for_count) that the N1QL port silently drops, so .all returned [] and .count raised NoMethodError. Rebuilt them by enumerating the parent documents (same work as the old view scan, client-side; an UNNEST query can replace it later). - Only reverse lookups (N1QL on a foreign key inside an embedded array) remain unsupported: tagged :embedded_in_reverse_lookup and excluded in spec_helper with an explanatory comment, replacing the broken :embedding filter line. embedding_spec.rb: 16 examples, 0 failures against live Couchbase 8.0.1 CE. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
181 lines
6.9 KiB
Ruby
181 lines
6.9 KiB
Ruby
require 'spec_helper'
|
|
# NOTE: this file used to carry `filter_run_excluding(:embedding)` — but no
|
|
# example was tagged :embedding, so nothing was ever excluded and the whole
|
|
# file ran (and failed) against Couchbase. The basic embedded flows are now
|
|
# fixed and expected green; only reverse lookups (finding embedded documents
|
|
# through an N1QL query on a foreign key inside the embedded array) remain
|
|
# unimplemented, and are tagged :embedded_in_reverse_lookup below (excluded in
|
|
# spec_helper).
|
|
|
|
describe 'Embedding' do
|
|
context 'initialized comments' do
|
|
before do
|
|
@post = Post.new
|
|
@post.embedded_comments = [
|
|
{ 'ruby_class' => 'EmbeddedComment', 'body' => 'body1' },
|
|
{ 'ruby_class' => 'EmbeddedComment', 'body' => 'body2' }
|
|
]
|
|
@post.save
|
|
end
|
|
|
|
it 'return a valid size' do
|
|
expect(@post.embedded_comments.size).to eq 2
|
|
post_reloaded = Post.find(@post.id)
|
|
expect(post_reloaded.embedded_comments.size).to eq 2
|
|
end
|
|
|
|
it 'delete comment using object' do
|
|
@post.remove_embedded_comment(@post.embedded_comments.first)
|
|
expect(@post.embedded_comments.size).to eq 1
|
|
post_reloaded = Post.find(@post.id)
|
|
expect(post_reloaded.embedded_comments.size).to eq 1
|
|
end
|
|
|
|
it 'get all emmbedded using .all' do
|
|
expect(EmbeddedComment.all.size).to eq 2
|
|
end
|
|
|
|
it 'get embedded object, not a hash' do
|
|
expect(EmbeddedComment.all.first).to be_a EmbeddedComment
|
|
end
|
|
|
|
it 'have a parent_object when loaded through all' do
|
|
expect(EmbeddedComment.all.first.parent_object).to eq @post
|
|
end
|
|
|
|
it 'save an instance' do
|
|
comment = @post.embedded_comments.first
|
|
comment.body = 'body-changed'
|
|
comment.save
|
|
comment_reloaded = Post.find(@post.id).embedded_comments.first
|
|
expect(comment_reloaded.body).to eq 'body-changed'
|
|
end
|
|
|
|
it 'change attribute when not loaded through parent object' do
|
|
embedded_comments = EmbeddedComment.all
|
|
embedded_comment = embedded_comments.first
|
|
embedded_comment.body = 'newbody'
|
|
embedded_comment.save
|
|
embedded_comments_reloaded = EmbeddedComment.all
|
|
expect(embedded_comments_reloaded.map(&:body)).to include('newbody')
|
|
end
|
|
|
|
it 'change attribute when saved through parent object' do
|
|
embedded_comments = EmbeddedComment.all
|
|
embedded_comment = embedded_comments.first
|
|
embedded_comment.body = 'newbody'
|
|
embedded_comment.parent_object.send(:attribute_will_change!, :embedded_comments)
|
|
embedded_comment.parent_object.save
|
|
embedded_comments_reloaded = EmbeddedComment.all
|
|
expect(embedded_comments_reloaded.map(&:body)).to include('newbody')
|
|
end
|
|
|
|
it 'delete comment using integer' do
|
|
end
|
|
|
|
it 'delete comment using integer string' do
|
|
end
|
|
|
|
it 'Count' do
|
|
expect(EmbeddedComment.count).to eq 2
|
|
end
|
|
end
|
|
|
|
context 'Creation of comment' do
|
|
before { @post = Post.create }
|
|
|
|
it 'not save when no parent is present' do
|
|
comment = EmbeddedComment.new(body: 'no parent')
|
|
expect(comment.save).to be false
|
|
expect(comment.errors[:post]).to include('no_parent')
|
|
end
|
|
|
|
it 'save when initialized with parent actual name initialization' do
|
|
comment = EmbeddedComment.new(body: 'no parent', post: @post)
|
|
expect(comment.save).to be true
|
|
expect(comment.post).to eq @post
|
|
expect(comment.parent_object).to eq @post
|
|
expect(@post.embedded_comments.include?(comment)).to be true
|
|
reloaded_post = Post.find(@post.id)
|
|
expect(reloaded_post.embedded_comments.include?(comment)).to be true
|
|
end
|
|
|
|
it 'save when initialized with parent object initialization' do
|
|
comment = EmbeddedComment.new(body: 'no parent', parent_object: @post)
|
|
expect(comment.save).to be true
|
|
expect(comment.post).to eq @post
|
|
expect(comment.parent_object).to eq @post
|
|
expect(@post.embedded_comments.include?(comment)).to be true
|
|
expect(comment.save).to be true
|
|
reloaded_post = Post.find(@post.id)
|
|
expect(reloaded_post.embedded_comments.include?(comment)).to be true
|
|
end
|
|
|
|
it 'save when parent object is assigned later with relation name' do
|
|
comment = EmbeddedComment.new(body: 'no parent')
|
|
comment.post = @post
|
|
expect(comment.save).to be true
|
|
expect(comment.post).to eq @post
|
|
expect(comment.parent_object).to eq @post
|
|
expect(@post.embedded_comments.include?(comment)).to be true
|
|
reloaded_post = Post.find(@post.id)
|
|
expect(reloaded_post.embedded_comments.include?(comment)).to be true
|
|
end
|
|
|
|
it 'save when parent object is assigned later with parent object assignment' do
|
|
comment = EmbeddedComment.new(body: 'no parent')
|
|
comment.parent_object = @post
|
|
expect(comment.save).to be true
|
|
expect(comment.post).to eq @post
|
|
expect(comment.parent_object).to eq @post
|
|
expect(@post.embedded_comments.include?(comment)).to be true
|
|
reloaded_post = Post.find(@post.id)
|
|
expect(reloaded_post.embedded_comments.include?(comment)).to be true
|
|
end
|
|
end
|
|
|
|
context 'belongs to strict_post', :embedded_in_reverse_lookup do
|
|
before do
|
|
@user = User.create(name: 'embedding user')
|
|
@strict_post = StrictPost.create(user: @user)
|
|
@post = Post.new
|
|
@post.embedded_comments = [
|
|
{ 'ruby_class' => 'EmbeddedComment', 'body' => 'body1' },
|
|
{ 'ruby_class' => 'EmbeddedComment', 'body' => 'body2' }
|
|
]
|
|
@post.save
|
|
end
|
|
|
|
it 'add embedded comments to strict_post' do
|
|
expect(@strict_post.save).to be true
|
|
@post.embedded_comments.each { |ec| ec.strict_post = @strict_post; ec.save }
|
|
strict_post_reloaded = StrictPost.find(@strict_post.id)
|
|
expect(strict_post_reloaded.embedded_comments.size).to eq @post.embedded_comments.size
|
|
end
|
|
|
|
it 'have strict_post as association' do
|
|
expect(@strict_post.save).to be true
|
|
@post.embedded_comments.each { |ec| ec.strict_post = @strict_post; ec.save }
|
|
post_reloaded = Post.find(@post.id)
|
|
comment_reloaded = post_reloaded.embedded_comments.first
|
|
expect(comment_reloaded.strict_post).to eq @strict_post
|
|
end
|
|
|
|
it 'have parent object when queried through relation' do
|
|
expect(@strict_post.save).to be true
|
|
@post.embedded_comments.each { |ec| ec.strict_post = @strict_post; ec.save }
|
|
strict_post_reloaded = StrictPost.find(@strict_post.id)
|
|
expect(strict_post_reloaded.embedded_comments.first).to eq @post.embedded_comments.first
|
|
expect(strict_post_reloaded.embedded_comments.first.post).to eq @post
|
|
end
|
|
|
|
it 'have actual object same as in parent object' do
|
|
expect(@strict_post.save).to be true
|
|
@post.embedded_comments.each { |ec| ec.strict_post = @strict_post; ec.save }
|
|
strict_post_reloaded = StrictPost.find(@strict_post.id)
|
|
embedded_comment = strict_post_reloaded.embedded_comments.first
|
|
expect(embedded_comment.parent_object.embedded_comments.map(&:object_id)).to include(embedded_comment.object_id)
|
|
end
|
|
end
|
|
end
|