(rename unit folder back as Unit)

This commit is contained in:
Adam Wathan
2017-05-02 15:42:41 -04:00
parent 1d8dfb6cf2
commit 0bcdd67a81
10 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
<?php
namespace Tests\Unit\Billing;
use Tests\TestCase;
use App\Billing\FakePaymentGateway;
class FakePaymentGatewayTest extends TestCase
{
use PaymentGatewayContractTests;
protected function getPaymentGateway()
{
return new FakePaymentGateway;
}
/** @test */
function running_a_hook_before_the_first_charge()
{
$paymentGateway = new FakePaymentGateway;
$timesCallbackRan = 0;
$paymentGateway->beforeFirstCharge(function ($paymentGateway) use (&$timesCallbackRan) {
$timesCallbackRan++;
$paymentGateway->charge(2500, $paymentGateway->getValidTestToken());
$this->assertEquals(2500, $paymentGateway->totalCharges());
});
$paymentGateway->charge(2500, $paymentGateway->getValidTestToken());
$this->assertEquals(1, $timesCallbackRan);
$this->assertEquals(5000, $paymentGateway->totalCharges());
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Tests\Unit\Billing;
use App\Billing\PaymentFailedException;
trait PaymentGatewayContractTests
{
abstract protected function getPaymentGateway();
/** @test */
function charges_with_a_valid_payment_token_are_successful()
{
$paymentGateway = $this->getPaymentGateway();
$newCharges = $paymentGateway->newChargesDuring(function ($paymentGateway) {
$paymentGateway->charge(2500, $paymentGateway->getValidTestToken());
});
$this->assertCount(1, $newCharges);
$this->assertEquals(2500, $newCharges->map->amount()->sum());
}
/** @test */
function can_get_details_about_a_successful_charge()
{
$paymentGateway = $this->getPaymentGateway();
$charge = $paymentGateway->charge(2500, $paymentGateway->getValidTestToken($paymentGateway::TEST_CARD_NUMBER));
$this->assertEquals(substr($paymentGateway::TEST_CARD_NUMBER, -4), $charge->cardLastFour());
$this->assertEquals(2500, $charge->amount());
}
/** @test */
function charges_with_an_invalid_payment_token_fail()
{
$paymentGateway = $this->getPaymentGateway();
$newCharges = $paymentGateway->newChargesDuring(function ($paymentGateway) {
try {
$paymentGateway->charge(2500, 'invalid-payment-token');
} catch (PaymentFailedException $e) {
return;
}
$this->fail("Charging with an invalid payment token did not throw a PaymentFailedException.");
});
$this->assertCount(0, $newCharges);
}
/** @test */
function can_fetch_charges_created_during_a_callback()
{
$paymentGateway = $this->getPaymentGateway();
$paymentGateway->charge(2000, $paymentGateway->getValidTestToken());
$paymentGateway->charge(3000, $paymentGateway->getValidTestToken());
$newCharges = $paymentGateway->newChargesDuring(function ($paymentGateway) {
$paymentGateway->charge(4000, $paymentGateway->getValidTestToken());
$paymentGateway->charge(5000, $paymentGateway->getValidTestToken());
});
$this->assertCount(2, $newCharges);
$this->assertEquals([5000, 4000], $newCharges->map->amount()->all());
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Tests\Unit\Billing;
use Tests\TestCase;
use App\Billing\StripePaymentGateway;
/**
* @group integration
*/
class StripePaymentGatewayTest extends TestCase
{
use PaymentGatewayContractTests;
protected function getPaymentGateway()
{
return new StripePaymentGateway(config('services.stripe.secret'));
}
}

141
tests/Unit/ConcertTest.php Normal file
View File

@@ -0,0 +1,141 @@
<?php
namespace Tests\Unit;
use App\Order;
use App\Ticket;
use App\Concert;
use Carbon\Carbon;
use Tests\TestCase;
use App\Exceptions\NotEnoughTicketsException;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ConcertTest extends TestCase
{
use DatabaseMigrations;
/** @test */
function can_get_formatted_date()
{
$concert = factory(Concert::class)->make([
'date' => Carbon::parse('2016-12-01 8:00pm'),
]);
$this->assertEquals('December 1, 2016', $concert->formatted_date);
}
/** @test */
function can_get_formatted_start_time()
{
$concert = factory(Concert::class)->make([
'date' => Carbon::parse('2016-12-01 17:00:00'),
]);
$this->assertEquals('5:00pm', $concert->formatted_start_time);
}
/** @test */
function can_get_ticket_price_in_dollars()
{
$concert = factory(Concert::class)->make([
'ticket_price' => 6750,
]);
$this->assertEquals('67.50', $concert->ticket_price_in_dollars);
}
/** @test */
function concerts_with_a_published_at_date_are_published()
{
$publishedConcertA = factory(Concert::class)->create(['published_at' => Carbon::parse('-1 week')]);
$publishedConcertB = factory(Concert::class)->create(['published_at' => Carbon::parse('-1 week')]);
$unpublishedConcert = factory(Concert::class)->create(['published_at' => null]);
$publishedConcerts = Concert::published()->get();
$this->assertTrue($publishedConcerts->contains($publishedConcertA));
$this->assertTrue($publishedConcerts->contains($publishedConcertB));
$this->assertFalse($publishedConcerts->contains($unpublishedConcert));
}
/** @test */
function can_add_tickets()
{
$concert = factory(Concert::class)->create();
$concert->addTickets(50);
$this->assertEquals(50, $concert->ticketsRemaining());
}
/** @test */
function tickets_remaining_does_not_include_tickets_associated_with_an_order()
{
$concert = factory(Concert::class)->create();
$concert->tickets()->saveMany(factory(Ticket::class, 30)->create(['order_id' => 1]));
$concert->tickets()->saveMany(factory(Ticket::class, 20)->create(['order_id' => null]));
$this->assertEquals(20, $concert->ticketsRemaining());
}
/** @test */
function trying_to_reserve_more_tickets_than_remain_throws_an_exception()
{
$concert = factory(Concert::class)->create()->addTickets(10);
try {
$reservation = $concert->reserveTickets(11, 'john@example.com');
} catch (NotEnoughTicketsException $e) {
$this->assertEquals(10, $concert->ticketsRemaining());
return;
}
$this->fail("Order succeeded even though there were not enough tickets remaining.");
}
/** @test */
function can_reserve_available_tickets()
{
$concert = factory(Concert::class)->create()->addTickets(3);
$this->assertEquals(3, $concert->ticketsRemaining());
$reservation = $concert->reserveTickets(2, 'john@example.com');
$this->assertCount(2, $reservation->tickets());
$this->assertEquals('john@example.com', $reservation->email());
$this->assertEquals(1, $concert->ticketsRemaining());
}
/** @test */
function cannot_reserve_tickets_that_have_already_been_purchased()
{
$concert = factory(Concert::class)->create()->addTickets(3);
$order = factory(Order::class)->create();
$order->tickets()->saveMany($concert->tickets->take(2));
try {
$concert->reserveTickets(2, 'john@example.com');
} catch (NotEnoughTicketsException $e) {
$this->assertEquals(1, $concert->ticketsRemaining());
return;
}
$this->fail("Reserving tickets succeeded even though the tickets were already sold.");
}
/** @test */
function cannot_reserve_tickets_that_have_already_been_reserved()
{
$concert = factory(Concert::class)->create()->addTickets(3);
$concert->reserveTickets(2, 'jane@example.com');
try {
$concert->reserveTickets(2, 'john@example.com');
} catch (NotEnoughTicketsException $e) {
$this->assertEquals(1, $concert->ticketsRemaining());
return;
}
$this->fail("Reserving tickets succeeded even though the tickets were already reserved.");
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Tests\Unit;
use App\Ticket;
use Tests\TestCase;
use App\HashidsTicketCodeGenerator;
class HashidsTicketCodeGeneratorTest extends TestCase
{
/** @test */
function ticket_codes_are_at_least_6_characters_long()
{
$ticketCodeGenerator = new HashidsTicketCodeGenerator('testsalt1');
$code = $ticketCodeGenerator->generateFor(new Ticket(['id' => 1]));
$this->assertTrue(strlen($code) >= 6);
}
/** @test */
function ticket_codes_can_only_contain_uppercase_letters()
{
$ticketCodeGenerator = new HashidsTicketCodeGenerator('testsalt1');
$code = $ticketCodeGenerator->generateFor(new Ticket(['id' => 1]));
$this->assertRegExp('/^[A-Z]+$/', $code);
}
/** @test */
function ticket_codes_for_the_same_ticket_id_are_the_same()
{
$ticketCodeGenerator = new HashidsTicketCodeGenerator('testsalt1');
$code1 = $ticketCodeGenerator->generateFor(new Ticket(['id' => 1]));
$code2 = $ticketCodeGenerator->generateFor(new Ticket(['id' => 1]));
$this->assertEquals($code1, $code2);
}
/** @test */
function ticket_codes_for_different_ticket_ids_are_different()
{
$ticketCodeGenerator = new HashidsTicketCodeGenerator('testsalt1');
$code1 = $ticketCodeGenerator->generateFor(new Ticket(['id' => 1]));
$code2 = $ticketCodeGenerator->generateFor(new Ticket(['id' => 2]));
$this->assertNotEquals($code1, $code2);
}
/** @test */
function ticket_codes_generated_with_different_salts_are_different()
{
$ticketCodeGenerator1 = new HashidsTicketCodeGenerator('testsalt1');
$ticketCodeGenerator2 = new HashidsTicketCodeGenerator('testsalt2');
$code1 = $ticketCodeGenerator1->generateFor(new Ticket(['id' => 1]));
$code2 = $ticketCodeGenerator2->generateFor(new Ticket(['id' => 1]));
$this->assertNotEquals($code1, $code2);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Tests\Unit\Mail;
use App\Order;
use Tests\TestCase;
use App\Mail\OrderConfirmationEmail;
class OrderConfirmationEmailTest extends TestCase
{
/** @test */
function email_contains_a_link_to_the_order_confirmation_page()
{
$order = factory(Order::class)->make([
'confirmation_number' => 'ORDERCONFIRMATION1234'
]);
$email = new OrderConfirmationEmail($order);
$rendered = $this->render($email);
// In Laravel 5.5...
// $rendered = $email->render();
$this->assertContains(url('/orders/ORDERCONFIRMATION1234'), $rendered);
}
/** @test */
function email_has_a_subject()
{
$order = factory(Order::class)->make();
$email = new OrderConfirmationEmail($order);
$this->assertEquals("Your TicketBeast Order", $email->build()->subject);
}
private function render($mailable)
{
$mailable->build();
return view($mailable->view, $mailable->buildViewData())->render();
}
}

86
tests/Unit/OrderTest.php Normal file
View File

@@ -0,0 +1,86 @@
<?php
namespace Tests\Unit;
use Mockery;
use App\Order;
use App\Ticket;
use Tests\TestCase;
use App\Billing\Charge;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Database\Eloquent\ModelNotFoundException;
class OrderTest extends TestCase
{
use DatabaseMigrations;
/** @test */
function creating_an_order_from_tickets_email_and_charge()
{
$charge = new Charge(['amount' => 3600, 'card_last_four' => '1234']);
$tickets = collect([
Mockery::spy(Ticket::class),
Mockery::spy(Ticket::class),
Mockery::spy(Ticket::class),
]);
$order = Order::forTickets($tickets, 'john@example.com', $charge);
$this->assertEquals('john@example.com', $order->email);
$this->assertEquals(3600, $order->amount);
$this->assertEquals('1234', $order->card_last_four);
$tickets->each->shouldHaveReceived('claimFor', [$order]);
}
/** @test */
function retrieving_an_order_by_confirmation_number()
{
$order = factory(Order::class)->create([
'confirmation_number' => 'ORDERCONFIRMATION1234',
]);
$foundOrder = Order::findByConfirmationNumber('ORDERCONFIRMATION1234');
$this->assertEquals($order->id, $foundOrder->id);
}
/** @test */
function retrieving_a_nonexistent_order_by_confirmation_number_throws_an_exception()
{
try {
Order::findByConfirmationNumber('NONEXISTENTCONFIRMATIONNUMBER');
} catch (ModelNotFoundException $e) {
return;
}
$this->fail('No matching order was found for the specified confirmation number, but an exception was not thrown.');
}
/** @test */
function converting_to_an_array()
{
$order = factory(Order::class)->create([
'confirmation_number' => 'ORDERCONFIRMATION1234',
'email' => 'jane@example.com',
'amount' => 6000,
]);
$order->tickets()->saveMany([
factory(Ticket::class)->create(['code' => 'TICKETCODE1']),
factory(Ticket::class)->create(['code' => 'TICKETCODE2']),
factory(Ticket::class)->create(['code' => 'TICKETCODE3']),
]);
$result = $order->toArray();
$this->assertEquals([
'confirmation_number' => 'ORDERCONFIRMATION1234',
'email' => 'jane@example.com',
'amount' => 6000,
'tickets' => [
['code' => 'TICKETCODE1'],
['code' => 'TICKETCODE2'],
['code' => 'TICKETCODE3'],
]
], $result);
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\RandomOrderConfirmationNumberGenerator;
class RandomOrderConfirmationNumberGeneratorTest extends TestCase
{
// Must be 24 characters long
// Can only contain uppercase letters and numbers
// Cannot contain ambiguous characters
// All order confirmation numbers must be unique
//
// ABCDEFGHJKLMNPQRSTUVWXYZ
// 23456789
/** @test */
function must_be_24_characters_long()
{
$generator = new RandomOrderConfirmationNumberGenerator;
$confirmationNumber = $generator->generate();
$this->assertEquals(24, strlen($confirmationNumber));
}
/** @test */
function can_only_contain_uppercase_letters_and_numbers()
{
$generator = new RandomOrderConfirmationNumberGenerator;
$confirmationNumber = $generator->generate();
$this->assertRegExp('/^[A-Z0-9]+$/', $confirmationNumber);
}
/** @test */
function cannot_contain_ambiguous_characters()
{
$generator = new RandomOrderConfirmationNumberGenerator;
$confirmationNumber = $generator->generate();
$this->assertFalse(strpos($confirmationNumber, '1'));
$this->assertFalse(strpos($confirmationNumber, 'I'));
$this->assertFalse(strpos($confirmationNumber, '0'));
$this->assertFalse(strpos($confirmationNumber, 'O'));
}
/** @test */
function confirmation_numbers_must_be_unique()
{
$generator = new RandomOrderConfirmationNumberGenerator;
$confirmationNumbers = array_map(function ($i) use ($generator) {
return $generator->generate();
}, range(1, 100));
$this->assertCount(100, array_unique($confirmationNumbers));
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Tests\Unit;
use Mockery;
use App\Ticket;
use App\Concert;
use Tests\TestCase;
use App\Reservation;
use App\Billing\FakePaymentGateway;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ReservationTest extends TestCase
{
use DatabaseMigrations;
/** @test */
function calculating_the_total_cost()
{
$tickets = collect([
(object) ['price' => 1200],
(object) ['price' => 1200],
(object) ['price' => 1200],
]);
$reservation = new Reservation($tickets, 'john@example.com');
$this->assertEquals(3600, $reservation->totalCost());
}
/** @test */
function retrieving_the_reservations_tickets()
{
$tickets = collect([
(object) ['price' => 1200],
(object) ['price' => 1200],
(object) ['price' => 1200],
]);
$reservation = new Reservation($tickets, 'john@example.com');
$this->assertEquals($tickets, $reservation->tickets());
}
/** @test */
function retrieving_the_customers_email()
{
$reservation = new Reservation(collect(), 'john@example.com');
$this->assertEquals('john@example.com', $reservation->email());
}
/** @test */
function reserved_tickets_are_released_when_a_reservation_is_cancelled()
{
$tickets = collect([
Mockery::spy(Ticket::class),
Mockery::spy(Ticket::class),
Mockery::spy(Ticket::class),
]);
$reservation = new Reservation($tickets, 'john@example.com');
$reservation->cancel();
foreach ($tickets as $ticket) {
$ticket->shouldHaveReceived('release');
}
}
/** @test */
function completing_a_reservation()
{
$concert = factory(Concert::class)->create(['ticket_price' => 1200]);
$tickets = factory(Ticket::class, 3)->create(['concert_id' => $concert->id]);
$reservation = new Reservation($tickets, 'john@example.com');
$paymentGateway = new FakePaymentGateway;
$order = $reservation->complete($paymentGateway, $paymentGateway->getValidTestToken());
$this->assertEquals('john@example.com', $order->email);
$this->assertEquals(3, $order->ticketQuantity());
$this->assertEquals(3600, $order->amount);
$this->assertEquals(3600, $paymentGateway->totalCharges());
}
}

49
tests/Unit/TicketTest.php Normal file
View File

@@ -0,0 +1,49 @@
<?php
namespace Tests\Unit;
use App\Order;
use App\Ticket;
use Tests\TestCase;
use App\Facades\TicketCode;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class TicketTest extends TestCase
{
use DatabaseMigrations;
/** @test */
function a_ticket_can_be_reserved()
{
$ticket = factory(Ticket::class)->create();
$this->assertNull($ticket->reserved_at);
$ticket->reserve();
$this->assertNotNull($ticket->fresh()->reserved_at);
}
/** @test */
function a_ticket_can_be_released()
{
$ticket = factory(Ticket::class)->states('reserved')->create();
$this->assertNotNull($ticket->reserved_at);
$ticket->release();
$this->assertNull($ticket->fresh()->reserved_at);
}
/** @test */
function a_ticket_can_be_claimed_for_an_order()
{
$order = factory(Order::class)->create();
$ticket = factory(Ticket::class)->create(['code' => null]);
TicketCode::shouldReceive('generateFor')->with($ticket)->andReturn('TICKETCODE1');
$ticket->claimFor($order);
$this->assertContains($ticket->id, $order->tickets->pluck('id'));
$this->assertEquals('TICKETCODE1', $ticket->code);
}
}