|
74 | 74 | # Create 12 posts for the topic |
75 | 75 | 12.times { |i| Fabricate(:post, topic: topic, post_number: i + 1, user: user) } |
76 | 76 |
|
77 | | - expect(Post).to receive(:where).with(topic_id: topic.id).and_call_original |
78 | | - expect_any_instance_of(ActiveRecord::Relation).to receive(:limit).with(10).and_call_original |
| 77 | + allow(Post).to receive(:where).with(topic_id: topic.id).and_call_original |
| 78 | + allow_any_instance_of(ActiveRecord::Relation).to receive(:limit).with(10).and_call_original |
79 | 79 |
|
80 | 80 | applier.topic_content_for_analysis(topic) |
| 81 | + |
| 82 | + expect(Post).to have_received(:where).with(topic_id: topic.id) |
81 | 83 | end |
82 | 84 | end |
83 | 85 |
|
|
128 | 130 | end |
129 | 131 |
|
130 | 132 | it "matches concepts and applies them to topic" do |
131 | | - allow(applier).to receive(:topic_content_for_analysis).with(topic).and_return( |
132 | | - "content about programming", |
133 | | - ) |
134 | | - |
135 | | - allow(applier).to receive(:match_concepts_to_content).with( |
136 | | - "content about programming", |
137 | | - %w[programming testing ruby], |
138 | | - ).and_return(["programming"]) |
139 | | - |
| 133 | + # Test the real implementation without stubbing internal methods |
140 | 134 | allow(InferredConcept).to receive(:where).with(name: ["programming"]).and_return([concept1]) |
141 | 135 |
|
142 | | - allow(applier).to receive(:apply_to_topic).with(topic, [concept1]) |
| 136 | + # Mock the LLM interaction |
| 137 | + persona_double = instance_spy(AiPersona) |
| 138 | + bot_double = instance_spy(DiscourseAi::Personas::Bot) |
| 139 | + structured_output_double = instance_spy(Object) |
| 140 | + llm_class_double = instance_spy(Class) |
| 141 | + |
| 142 | + allow(AiPersona).to receive_message_chain(:all_personas, :find, :new).and_return( |
| 143 | + persona_double, |
| 144 | + ) |
| 145 | + allow(persona_double).to receive(:class).and_return(llm_class_double) |
| 146 | + allow(llm_class_double).to receive(:default_llm_id).and_return(llm_model.id) |
| 147 | + allow(LlmModel).to receive(:find).and_return(llm_model) |
| 148 | + allow(DiscourseAi::Personas::Bot).to receive(:as).and_return(bot_double) |
| 149 | + allow(bot_double).to receive(:reply).and_yield( |
| 150 | + structured_output_double, |
| 151 | + nil, |
| 152 | + :structured_output, |
| 153 | + ) |
| 154 | + allow(structured_output_double).to receive(:read_buffered_property).with( |
| 155 | + :matching_concepts, |
| 156 | + ).and_return(["programming"]) |
143 | 157 |
|
144 | 158 | result = applier.match_existing_concepts(topic) |
145 | 159 | expect(result).to eq([concept1]) |
|
166 | 180 | end |
167 | 181 |
|
168 | 182 | it "matches concepts and applies them to post" do |
169 | | - allow(applier).to receive(:post_content_for_analysis).with(post).and_return( |
170 | | - "content about testing", |
171 | | - ) |
172 | | - |
173 | | - allow(applier).to receive(:match_concepts_to_content).with( |
174 | | - "content about testing", |
175 | | - %w[programming testing ruby], |
176 | | - ).and_return(["testing"]) |
177 | | - |
| 183 | + # Test the real implementation without stubbing internal methods |
178 | 184 | allow(InferredConcept).to receive(:where).with(name: ["testing"]).and_return([concept2]) |
179 | 185 |
|
180 | | - allow(applier).to receive(:apply_to_post).with(post, [concept2]) |
| 186 | + # Mock the LLM interaction |
| 187 | + persona_double = instance_spy(AiPersona) |
| 188 | + bot_double = instance_spy(DiscourseAi::Personas::Bot) |
| 189 | + structured_output_double = instance_spy(Object) |
| 190 | + llm_class_double = instance_spy(Class) |
| 191 | + |
| 192 | + allow(AiPersona).to receive_message_chain(:all_personas, :find, :new).and_return( |
| 193 | + persona_double, |
| 194 | + ) |
| 195 | + allow(persona_double).to receive(:class).and_return(llm_class_double) |
| 196 | + allow(llm_class_double).to receive(:default_llm_id).and_return(llm_model.id) |
| 197 | + allow(LlmModel).to receive(:find).and_return(llm_model) |
| 198 | + allow(DiscourseAi::Personas::Bot).to receive(:as).and_return(bot_double) |
| 199 | + allow(bot_double).to receive(:reply).and_yield( |
| 200 | + structured_output_double, |
| 201 | + nil, |
| 202 | + :structured_output, |
| 203 | + ) |
| 204 | + allow(structured_output_double).to receive(:read_buffered_property).with( |
| 205 | + :matching_concepts, |
| 206 | + ).and_return(["testing"]) |
181 | 207 |
|
182 | 208 | result = applier.match_existing_concepts_for_post(post) |
183 | 209 | expect(result).to eq([concept2]) |
|
195 | 221 | it "uses ConceptMatcher persona to match concepts" do |
196 | 222 | content = "This is about Ruby programming" |
197 | 223 | concept_list = %w[programming testing ruby] |
198 | | - structured_output_double = double("StructuredOutput") |
| 224 | + structured_output_double = instance_spy(Object) |
199 | 225 |
|
200 | | - persona_class_double = double("ConceptMatcherClass") |
201 | | - persona_double = double("ConceptMatcher") |
202 | | - bot_double = double("Bot") |
| 226 | + persona_class_double = instance_spy(Class) |
| 227 | + persona_double = instance_spy(AiPersona) |
| 228 | + bot_double = instance_spy(DiscourseAi::Personas::Bot) |
203 | 229 |
|
204 | | - expect(AiPersona).to receive_message_chain(:all_personas, :find).and_return( |
| 230 | + allow(AiPersona).to receive_message_chain(:all_personas, :find).and_return( |
205 | 231 | persona_class_double, |
206 | 232 | ) |
207 | | - expect(persona_class_double).to receive(:new).and_return(persona_double) |
208 | | - expect(persona_double).to receive(:class).and_return(persona_class_double) |
209 | | - expect(persona_class_double).to receive(:default_llm_id).and_return(llm_model.id) |
210 | | - expect(LlmModel).to receive(:find).and_return(llm_model) |
211 | | - expect(DiscourseAi::Personas::Bot).to receive(:as).and_return(bot_double) |
212 | | - expect(bot_double).to receive(:reply).and_yield( |
| 233 | + allow(persona_class_double).to receive(:new).and_return(persona_double) |
| 234 | + allow(persona_double).to receive(:class).and_return(persona_class_double) |
| 235 | + allow(persona_class_double).to receive(:default_llm_id).and_return(llm_model.id) |
| 236 | + allow(LlmModel).to receive(:find).and_return(llm_model) |
| 237 | + allow(DiscourseAi::Personas::Bot).to receive(:as).and_return(bot_double) |
| 238 | + allow(bot_double).to receive(:reply).and_yield( |
213 | 239 | structured_output_double, |
214 | 240 | nil, |
215 | 241 | :structured_output, |
216 | 242 | ) |
217 | | - expect(structured_output_double).to receive(:read_buffered_property).with( |
| 243 | + allow(structured_output_double).to receive(:read_buffered_property).with( |
218 | 244 | :matching_concepts, |
219 | 245 | ).and_return(%w[programming ruby]) |
220 | 246 |
|
221 | 247 | result = applier.match_concepts_to_content(content, concept_list) |
222 | 248 | expect(result).to eq(%w[programming ruby]) |
| 249 | + |
| 250 | + expect(bot_double).to have_received(:reply) |
| 251 | + expect(structured_output_double).to have_received(:read_buffered_property).with( |
| 252 | + :matching_concepts, |
| 253 | + ) |
223 | 254 | end |
224 | 255 |
|
225 | 256 | it "handles no structured output gracefully" do |
|
0 commit comments