The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/perl

use lib 't/lib';
use strict;
use warnings;
use Test::More;
use MongoDBx::Class;
use DateTime;

my $dbx = MongoDBx::Class->new(namespace => 'MongoDBxTestSchema');

# temporary bypass, should be removed when I figure out why tests can't find the schema
if (scalar(keys %{$dbx->doc_classes}) != 5) {
	plan skip_all => "Temporary skip due to schema not being found";
} else {
	plan tests => 32;
}

SKIP: {
	is(scalar(keys %{$dbx->doc_classes}), 5, 'successfully loaded schema');

	SKIP: {
		my $conn;
		eval { $conn = $dbx->connect };

		skip "Can't connect to MongoDB server", 31 if $@;

		$conn->safe(1);
		is($conn->safe, 1, "Using safe operations by default");

		my $db = $conn->get_database('mongodbx_class_test');
		$db->drop;
		my $novels_coll = $db->get_collection('novels');

		$novels_coll->ensure_index([ title => 1, year => -1 ]);
		
		my $novel = $novels_coll->insert({
			_class => 'Novel',
			title => 'The Valley of Fear',
			year => 1914,
			author => {
				first_name => 'Arthur',
				middle_name => 'Conan',
				last_name => 'Doyle',
			},
			added => DateTime->now,
			tags => [
				{ category => 'mystery', subcategory => 'thriller' },
				{ category => 'mystery', subcategory => 'detective' },
				{ category => 'crime', subcategory => 'fiction' },
			],
		});

		is(ref($novel->_id), 'MongoDB::OID', 'document successfully inserted');

		is(ref($novel->added), 'DateTime', 'added attribute successfully parsed');

		is($novel->author->name, 'Arthur Conan Doyle', 'embedded document works');

		my $synopsis = $db->get_collection('synopsis')->insert({
			_class => 'Synopsis',
			novel => $novel,
			text => "The Valley of Fear is the final Sherlock Holmes novel by Sir Arthur Conan Doyle. The story was first published in the Strand Magazine between September 1914 and May 1915. The first book edition was published in New York on 27 February 1915.",
		});

		is(ref($synopsis->_id), 'MongoDB::OID', 'successfully created a synopsis');
		is($synopsis->novel->_id, $novel->_id, 'reference from synopsis to novel correct');

		my @reviews = $db->get_collection('reviews')->batch_insert([
			{
				_class => 'Review',
				novel => $novel,
				reviewer => 'Some Guy',
				text => 'I really liked it!',
				score => 5,
			}, 
			{
				_class => 'Review',
				novel => $novel,
				reviewer => 'Some Other Guy',
				text => 'It was okay.',
				score => 3,
			}, 
			{
				_class => 'Review',
				novel => $novel,
				reviewer => 'Totally Different Guy',
				text => 'Man, that just sucked!',
				score => 1,
			}
		]);

		is(scalar(@reviews), 3, 'successfully created three reviews');

		my ($total_score, $avg_score) = (0, 0);
		foreach (@reviews) {
			$total_score += $_->score || 0;
		}
		$avg_score = $total_score / scalar(@reviews);
		is($avg_score, 3, 'average score correct');

		$novel->update({ year => 1915, 'author.middle_name' => 'Xoxa' });
		is($novel->year, 1915, "novel's year successfully changed");
		is($novel->author->middle_name, 'Xoxa', "author's middle name successfully changed");

		is_deeply([$novel->_attributes], [qw/_id added author related_novels review_count tags title year/], '_attributes okay');
		is_deeply([$novel->author->_attributes], [qw/first_name last_name middle_name/], 'embedded _attributes okay');

		my $found_novel = $db->get_collection('novels')->find_one($novel->id);
		is($found_novel->reviews->count, 3, 'joins_many works correctly');
		is(ref $found_novel->reviews, 'MongoDBx::Class::Cursor', 'joins_many attribute gives a MongoDBx::Class::Cursor');

		my @all_reviews = $found_novel->reviews->all;
		is(scalar @all_reviews, 3, 'all method gives an array containing all reviews');
		for( @all_reviews ) {
			is(ref $_, 'MongoDBxTestSchema::Review', 'array element is MongoDBxTestSchema::Review objects');
		}

		$found_novel->set_year(1914);
		$found_novel->author->set_middle_name('Conan');
		$found_novel->update();

		is($db->get_collection('novels')->find_one($found_novel->_id)->year, 1914, "novel's year successfully changed back");
		is($db->get_collection('novels')->find_one({ _id => MongoDB::OID->new(value => $found_novel->oid) })->author->middle_name, 'Conan', "author's middle name successfully changed back");
		
		is($found_novel->added->year, DateTime->now->year, 'DateTime objects correctly parsed by MongoDBx::Class::ParsedAttribute::DateTime');

		$synopsis->delete;

		my $syns = $db->get_collection('synopsis')->find({ 'novel.$id' => $novel->_id });
		is($syns->count, 0, 'Synopsis successfully removed');
		
		$db->get_collection('reviews')->update({ 'novel.$id' => $novel->_id }, { '$set' => { reviewer => 'John John' }, '$inc' => { score => 3 } }, { multiple => 1 });
		my @scores;
		my $john_john = 1;
		foreach ($novel->reviews->sort([ score => -1 ])->all) {
			undef $john_john if $_->reviewer ne 'John John';
			push(@scores, $_->score);
		}
		is($john_john, 1, "Successfully replaced reviewer for all reviews");
		is_deeply(\@scores, [8, 6, 4], "Successfully increased all scores by three");

		# Test transient Attributes
		is($novel->review_count, 3, 'Correct number of reviews found');
		$novel->review_count(4);
		is($novel->review_count, 4, 'Set number of reviews is correct');
		$novel->update;

		# read novel from db again, without expanding it, to
		# verify the review_count attribute was not saved
		my $novel_fetched = $db->get_collection('novels')->find({ _id => $novel->_id })->next(1);
		ok(!exists $novel_fetched->{review_count}, 'Transient value was not saved in DB');

		$novel = $db->get_collection('novels')->find_one($novel->id); # refresh from DB
		$novel->update({ review_count => 4 });
		is($novel->review_count, 3, 'Transient value not updated'); # really the correct behaviour?

		# refresh again
		$novel_fetched = $db->get_collection('novels')->find({ _id => $novel->_id })->next(1);
		ok(!exists $novel_fetched->{review_count}, 'Transient value was not saved in DB');
		
		my $novel2 = $novels_coll->insert({
			_class => 'Novel',
			title => 'Modern Perl',
			year => 2010,
			author => {
				first_name => '',
				middle_name => 'chromatic',
				last_name => '',
			},
			added => DateTime->now,
			tags => [
				{ category => 'programming', subcategory => 'perl' },
			],
			review_count => 2,
		});

		# refresh novel 2 without expanding
		my $novel2_fetched = $db->get_collection('novels')->find({ _id => $novel2->_id })->next(1);
		ok(!exists $novel2_fetched->{review_count}, 'Transient value was not saved in DB');

		is($novel2->review_count, 0, 'Transient value is not inflated');

		$db->drop;
	}
}

done_testing();