db = Mockery::mock(\wpdb::class); $this->db->prefix = 'wp_'; $this->repo = new QuestionRepository($this->db); } public function testInsertWithNullOptionsStoresNull(): void { Functions\expect('current_time')->with('mysql')->andReturn('2026-04-01 12:00:00'); $this->db->shouldReceive('insert') ->once() ->with( 'wp_us_questions', Mockery::on(static function (array $data): bool { return $data['offering_id'] === 7 && $data['label'] === 'Your level?' && $data['field_type'] === Question::FIELD_TEXT && $data['options'] === null && $data['is_required'] === 0 && $data['created_at'] === '2026-04-01 12:00:00'; }), Mockery::type('array') ); $this->db->insert_id = 21; $question = new Question(7, 'Your level?'); self::assertSame(21, $this->repo->insert($question)); } public function testInsertEncodesOptionsAsJson(): void { Functions\expect('current_time')->andReturn('2026-04-01 12:00:00'); Functions\expect('wp_json_encode') ->once() ->with(['Beginner', 'Advanced']) ->andReturn('["Beginner","Advanced"]'); $this->db->shouldReceive('insert') ->once() ->with( 'wp_us_questions', Mockery::on(static fn (array $data): bool => $data['options'] === '["Beginner","Advanced"]'), Mockery::type('array') ); $this->db->insert_id = 22; $question = new Question( offeringId: 7, label: 'Pick a level', fieldType: Question::FIELD_SELECT, options: ['Beginner', 'Advanced'], ); self::assertSame(22, $this->repo->insert($question)); } public function testUpdateReturnsTrueOnSuccess(): void { $this->db->shouldReceive('update') ->once() ->with( 'wp_us_questions', Mockery::on(static fn (array $data): bool => $data['label'] === 'Renamed' && $data['is_required'] === 1), ['id' => 5], Mockery::type('array'), ['%d'] ) ->andReturn(1); $question = new Question(7, 'Renamed', isRequired: true, id: 5); self::assertTrue($this->repo->update(5, $question)); } public function testFindByOfferingActiveOnlyPreparesQuery(): void { $this->db->shouldReceive('prepare') ->once() ->with( Mockery::pattern('/offering_id = %d AND is_active = %d/'), Mockery::on(static fn (array $p): bool => $p === [7, 1]) ) ->andReturn('SELECT ...'); $this->db->shouldReceive('get_results')->andReturn([]); self::assertSame([], $this->repo->findByOffering(7, activeOnly: true)); } public function testFindByOfferingReturnsQuestions(): void { $row = (object) [ 'id' => '3', 'offering_id' => '7', 'label' => 'Q', 'field_type' => Question::FIELD_TEXT, 'options' => null, 'is_required' => '0', 'sort_order' => '0', 'is_active' => '1', ]; $this->db->shouldReceive('prepare')->andReturn('SELECT ...'); $this->db->shouldReceive('get_results')->andReturn([$row]); $questions = $this->repo->findByOffering(7); self::assertCount(1, $questions); self::assertInstanceOf(Question::class, $questions[0]); } public function testFindByIdReturnsNullWhenNotFound(): void { $this->db->shouldReceive('prepare')->andReturn('SELECT ...'); $this->db->shouldReceive('get_row')->andReturn(null); self::assertNull($this->repo->findById(99)); } public function testDeleteCallsWpdbDelete(): void { $this->db->shouldReceive('delete') ->once() ->with('wp_us_questions', ['id' => 4], ['%d']) ->andReturn(1); self::assertTrue($this->repo->delete(4)); } }