code:
<?php
declare(strict_types=1);

namespace Argo\App\Config;

use Argo\Domain\Json;

class SaveConfigTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testNotFound()
    {
        $name = 'no-such-config';
        $payload = $this->invoke($name, '');
        $this->assertNotFound($payload);
    }

    public function testInvalid()
    {
        $name = 'general';

        $payload = $this->invoke($name, '');
        $this->assertInvalid($payload, 'The config cannot be blank.');

        $payload = $this->invoke($name, '{"foo": xxx}');
        $this->assertInvalid($payload, 'Parse error');
    }

    public function testUpdated()
    {
        $name = 'general';
        $text = '{"foo": "bar"}';
        $payload = $this->invoke($name, $text);
        $this->assertUpdated($payload);

        $item = $payload->getResult()['item'];
        $this->assertSame('config', $item->type);
        $this->assertSame('general', $item->relId);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Draft;

class AddDraftTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testCreated_withoutTitle() : void
    {
        $payload = $this->invoke('');
        $this->assertCreated($payload);
        $actual = $payload->getResult()['item']->getArrayCopy();
        $expect = [
            'id' => '_draft/00010203T123456',
            'relId' => '00010203T123456',
            'type' => 'draft',
            'href' => '/_draft/00010203T123456/',
            'data' => (object) [
                'title' => 'Untitled',
                'author' => 'boshag',
                'tags' => [
                    0 => 'general',
                ],
                'created' => '0001-02-03 12:34:56 UTC',
                'updated' => [
                    0 => '0001-02-03 12:34:56 UTC',
                ],
                'markup' => 'markdown',
            ],
            'prev' => null,
            'next' => null,
        ];
        $this->assertEquals($expect, $actual);
    }

    public function testCreated_withTitle() : void
    {
        $payload = $this->invoke('Title Title');
        $this->assertCreated($payload);
        $actual = $payload->getResult()['item']->getArrayCopy();
        $expect = [
            'id' => '_draft/00010203T123456',
            'relId' => '00010203T123456',
            'type' => 'draft',
            'href' => '/_draft/00010203T123456/',
            'data' => (object) [
                'title' => 'Title Title',
                'author' => 'boshag',
                'tags' => [
                    0 => 'general',
                ],
                'created' => '0001-02-03 12:34:56 UTC',
                'updated' => [
                    0 => '0001-02-03 12:34:56 UTC',
                ],
                'markup' => 'markdown',
            ],
            'prev' => null,
            'next' => null,
        ];
        $this->assertEquals($expect, $actual);
    }
}

repo_name: tkkonline/php

license: mit


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Draft;

class FetchDraftTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-draft');
        $this->assertNotFound($payload);
    }

    public function testFound() : void
    {
        $addDraft = $this->container->get(AddDraft::CLASS);
        $created = $addDraft('Title Title');
        $relId = $created->getResult()['item']->relId;

        $found = $this->invoke($relId);
        $this->assertFound($found);
        $this->assertEquals(
            $created->getResult()['item'],
            $found->getResult()['draft']
        );
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Draft;

class PublishDraftTest extends \Argo\App\TestCase
{
    protected $draft;

    protected $body = 'Post body.';

    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();

        $addDraft = $this->container->get(AddDraft::CLASS);
        $created = $addDraft('Title Title');
        $this->draft = $created->getResult()['item'];
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-draft', [], '');
        $this->assertNotFound($payload);
    }

    public function testInvalid() : void
    {
        $payload = $this->invoke(
            $this->draft->relId,
            [
                'title' => '!@#$',
            ],
            $this->body
        );

        $this->assertInvalid($payload, 'Cannot create post URL from draft title');
    }

    public function testCreated() : void
    {
        $now = $this->modDateTimeNow("+10 minutes");

        $payload = $this->invoke(
            $this->draft->relId,
            (array) $this->draft->data,
            $this->body
        );

        $this->assertCreated($payload);
        $actual = $payload->getResult()['item']->getArrayCopy();
        $expect = [
            'prev' => null,
            'next' => null,
            'lastUpdated' => $now,
            'id' => 'post/0001/02/03/title-title',
            'relId' => '0001/02/03/title-title',
            'type' => 'post',
            'href' => '/post/0001/02/03/title-title/',
            'data' => (object) [
                'title' => 'Title Title',
                'author' => 'boshag',
                'tags' => [
                    0 => 'general',
                ],
                'created' => $now,
                'updated' => [
                    0 => $now,
                ],
                'markup' => 'markdown',
                'tags' => [
                    'general'
                ],
            ],
            'postIndexKey' => null,
        ];
        $this->assertEquals($expect, $actual);
    }

    public function testInvalid_postAlreadyExists() : void
    {
        $payload = $this->invoke(
            $this->draft->relId,
            (array) $this->draft->data,
            $this->body
        );

        $this->assertCreated($payload);

        $addDraft = $this->container->get(AddDraft::CLASS);
        $created = $addDraft('Title Title');
        $draft2 = $created->getResult()['item'];

        $payload = $this->invoke(
            $draft2->relId,
            (array) $draft2->data,
            $this->body
        );

        $this->assertInvalid($payload, "A post at '0001/02/03/title-title' already exists.");
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Draft;

class SaveDraftTest extends \Argo\App\TestCase
{
    protected $draft;

    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();

        $addDraft = $this->container->get(AddDraft::CLASS);
        $created = $addDraft('Title Title');
        $this->draft = $created->getResult()['item'];
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-draft', [], '');
        $this->assertNotFound($payload);
    }

    public function testInvalid() : void
    {
        $payload = $this->invoke(
            $this->draft->relId,
            [
                'tags' => 'foo bar',
            ],
            'New body.'
        );

        $this->assertInvalid($payload);
    }

    public function testUpdated() : void
    {
        $payload = $this->invoke(
            $this->draft->relId,
            [
                'title' => 'New Title!',
            ],
            'New body.'
        );

        $this->assertUpdated($payload);

        $actual = $payload->getResult()['item'];
        $this->assertSame('New Title!', $actual->title);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Draft;

class TrashDraftTest extends \Argo\App\TestCase
{
    protected $draft;

    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();

        $addDraft = $this->container->get(AddDraft::CLASS);
        $created = $addDraft('Title Title');
        $this->draft = $created->getResult()['item'];
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-draft');
        $this->assertNotFound($payload);
    }

    public function testDeleted() : void
    {
        $payload = $this->invoke($this->draft->relId);

        $this->assertDeleted($payload);

        $actual = $payload->getResult()['item'];
        $this->assertSame($this->draft->relId, $actual->relId);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Page;

class AddPageTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testInvalid() : void
    {
        $payload = $this->invoke('!@#$');
        $this->assertInvalid($payload, 'Page ID has invalid characters; use only a-z, 0-9, and dashes.');

        $payload = $this->invoke('foo/bar/baz/dib');
        $this->assertInvalid(
            $payload,
            "Cannot create page 'foo/bar/baz/dib' without parent page 'foo/bar/baz' first."
        );
    }

    public function testCreated() : void
    {
        $payload = $this->invoke('/foo/');
        $this->assertCreated($payload);
        $actual = $payload->getResult()['item']->getArrayCopy();
        $expect = [
            'id' => 'foo',
            'relId' => 'foo',
            'type' => 'page',
            'href' => '/foo/',
            'data' => (object) [
                'title' => 'Foo',
                'author' => 'boshag',
                'created' => '0001-02-03 12:34:56 UTC',
                'updated' => [
                    0 => '0001-02-03 12:34:56 UTC',
                ],
                'markup' => 'markdown',
                'tags' => [],
            ],
            'prev' => null,
            'next' => null,
        ];
        $this->assertEquals($expect, $actual);

        $this->modDateTimeNow('+10 minutes');

        $payload = $this->invoke('/foo/bar');
        $this->assertCreated($payload);

        $actual = $payload->getResult()['item']->getArrayCopy();
        $expect = [
            'id' => 'foo/bar',
            'relId' => 'foo/bar',
            'type' => 'page',
            'href' => '/foo/bar/',
            'data' => (object) [
                'title' => 'Foo Bar',
                'author' => 'boshag',
                'created' => '0001-02-03 12:44:56 UTC',
                'updated' => [
                    0 => '0001-02-03 12:44:56 UTC',
                ],
                'markup' => 'markdown',
                'tags' => [],
            ],
            'prev' => null,
            'next' => null,
        ];
        $this->assertEquals($expect, $actual);
    }

    public function testInvalid_pageAlreadyExists()
    {
        $payload = $this->invoke('/foo/');
        $this->assertCreated($payload);

        $payload = $this->invoke('foo');
        $this->assertInvalid($payload, 'Page foo already exists.');
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Page;

class FetchPageTest extends \Argo\App\TestCase
{
    protected $page;

    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();

        $addPage = $this->container->get(AddPage::CLASS);
        $payload = $addPage('foo');
        $this->assertCreated($payload);
        $this->page = $payload->getResult()['item'];
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-page');
        $this->assertNotFound($payload);
    }

    public function testFound() : void
    {
        $payload = $this->invoke('foo');
        $this->assertFound($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Page;

class FetchPagesTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testFound() : void
    {
        for ($i = 0; $i < 10; $i++) {
            $addPage = $this->container->get(AddPage::CLASS);
            $payload = $addPage("foo-{$i}");
            $this->assertCreated($payload);
        }

        $payload = $this->invoke();
        $this->assertFound($payload);
        $pages = $payload->getResult()['pages'];
        $this->assertCount(10, $pages);

        $i = 0;
        foreach ($pages as $page) {
            $this->assertEquals("foo-{$i}", $page->relId);
            $i ++;
        }
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Page;

class SavePageTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();

        $addPage = $this->container->get(AddPage::CLASS);
        $payload = $addPage('foo');
        $this->assertCreated($payload);
        $this->page = $payload->getResult()['item'];
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-page', [], '');
        $this->assertNotFound($payload);
    }

    public function testUpdated() : void
    {
        $id = 'foo';
        $data = ['title' => 'New Title'];
        $body = 'New body.';

        $payload = $this->invoke($id, $data, $body);
        $this->assertUpdated($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Page;

class TrashPageTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();

        $addPage = $this->container->get(AddPage::CLASS);
        $payload = $addPage('foo');
        $this->assertCreated($payload);
        $this->page = $payload->getResult()['item'];
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-page');
        $this->assertNotFound($payload);
    }

    public function testDeleted() : void
    {
        $payload = $this->invoke('foo');
        $this->assertDeleted($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Post;

class FetchPostTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-post');
        $this->assertNotFound($payload);
    }

    public function testFound() : void
    {
        $payload = $this->invoke('0001/02/03/sample-post');
        $this->assertFound($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Post;

use Argo\Domain\Content\Post\Post;
use Argo\Infra\BuildFactory;

class FetchPostsTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();

        $samplePost = $this->content->posts->getItem('0001/02/03/sample-post');
        $this->content->posts->trash($samplePost);

        for ($i = 10; $i < 40; $i ++) {
            $date = $this->dateTime->ymd();
            $this->post = new Post(
                Post::absId("{$date}/title-{$i}"),
                [
                    'title' => "Title {$i}",
                    'author' => 'boshag',
                    'tags' => 'general',
                ]
            );
            $this->content->posts->save($this->post, "Body {$i}.");
            $this->modDateTimeNow("+10 minutes");
        }
        $this->container->get(BuildFactory::CLASS)->new()->all();
    }

    public function test() : void
    {
        for ($i = 1; $i < 4; $i ++) {
            $payload = $this->invoke($i);
            $this->assertFound($payload);
            $result = $payload->getResult();
            $this->assertEquals($i, $result['pageNum']);
            // $this->assertEquals(4, $result['pageCount']);
            $this->assertCount(10, $result['posts']);
        }

        $payload = $this->invoke(4);
        $this->assertNotFound($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Post;

class SavePostTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-post', [], '');
        $this->assertNotFound($payload);
    }

    public function testUpdated() : void
    {
        $payload = $this->invoke(
            '0001/02/03/sample-post',
            [
                'title' => 'New Title',
            ],
            'New body.'
        );
        $this->assertUpdated($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Post;

class TrashPostTest extends \Argo\App\TestCase
{
    protected $post;

    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-post');
        $this->assertNotFound($payload);
    }

    public function testDeleted() : void
    {
        $payload = $this->invoke('0001/02/03/sample-post');
        $this->assertDeleted($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Tag;

class AddTagTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testInvalid() : void
    {
        $payload = $this->invoke('  ');
        $this->assertInvalid($payload, "Tag name cannot be blank.");

        $payload = $this->invoke('!@#$');
        $this->assertInvalid($payload, 'Tag ID has invalid characters; use only a-z, 0-9, and dashes.');

        $payload = $this->invoke(' general ');
        $this->assertInvalid($payload, "Tag 'general' already exists.");
    }

    public function testCreated() : void
    {
        $payload = $this->invoke('foo');
        $this->assertCreated($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Tag;

class FetchTagTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-tag');
        $this->assertNotFound($payload);
    }

    public function testFound() : void
    {
        $payload = $this->invoke('general');
        $this->assertFound($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Tag;

class FetchTagsTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function test() : void
    {
        for ($i = 10; $i < 20; $i ++) {
            $addTag = $this->container->get(AddTag::CLASS);
            $payload = $addTag("foo-{$i}");
            $this->assertCreated($payload);
        }

        $payload = $this->invoke();
        $this->assertFound($payload);
        $this->assertCount(11, $payload->getResult()['tags']);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Content\Tag;

class SaveTagTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testNotFound() : void
    {
        $payload = $this->invoke('no-such-tag', [], '');
        $this->assertNotFound($payload);
    }

    public function testUpdated() : void
    {
        $payload = $this->invoke(
            'general',
            [
                'title' => 'New Title',
            ],
            'New body.'
        );
        $this->assertUpdated($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App;

class DashboardTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testFound() : void
    {
        $payload = $this->invoke();
        $this->assertFound($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App;

use Exception;

class FakeUseCase extends UseCase
{
    public function exec()
    {
        throw new Exception('fake error');
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App;

class FakeUseCaseTest extends \Argo\App\TestCase
{
    public function test()
    {
        $payload = $this->invoke();
        $this->assertError($payload, 'fake error');
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Import;

use SapiUpload;

class ImportTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testError() : void
    {
        $file = __DIR__ . '/wordpress.export.xml';
        $upload = new SapiUpload(
            basename($file),
            'application/xml',
            filesize($file),
            $file,
            UPLOAD_ERR_PARTIAL
        );

        $payload = $this->invoke($upload);
        $this->assertError($payload, "The uploaded file was only partially uploaded.");
    }

    public function testSuccess() : void
    {
        $file = __DIR__ . '/wordpress.export.xml';
        $upload = new SapiUpload(
            basename($file),
            'application/xml',
            filesize($file),
            $file,
            0
        );

        $payload = $this->invoke($upload);
        $this->assertProcessing($payload);
    }
}


code:
<?xml version="1.0" encoding="UTF-8" ?>
<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
<!-- It contains information about your site's posts, pages, comments, categories, and other content. -->
<!-- You may use this file to transfer that content from one site to another. -->
<!-- This file is not intended to serve as a complete backup of your site. -->

<!-- To import this information into a WordPress site follow these steps: -->
<!-- 1. Log in to that site as an administrator. -->
<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
<!-- 3. Install the "WordPress" importer from the list. -->
<!-- 4. Activate & Run Importer. -->
<!-- 5. Upload this file using the form provided on that page. -->
<!-- 6. You will first be asked to map the authors in this export file to users -->
<!--    on the site. For each author, you may choose to map to an -->
<!--    existing user on the site or to create a new user. -->
<!-- 7. WordPress will then import each of the posts, pages, comments, categories, etc. -->
<!--    contained in this file into your site. -->

<!-- generator="WordPress/4.8.2" created="2017-10-11 20:30" -->
<rss version="2.0"
	xmlns:excerpt="http://wordpress.org/export/1.2/excerpt/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:wp="http://wordpress.org/export/1.2/"
>

<channel>
	<title>Bolivar Shagnasty</title>
	<link>http://example.com</link>
	<description>Blog descriptiont text.</description>
	<pubDate>Wed, 11 Oct 2017 20:30:04 +0000</pubDate>
	<language>en-US</language>
	<wp:wxr_version>1.2</wp:wxr_version>
	<wp:base_site_url>http://example.com</wp:base_site_url>
	<wp:base_blog_url>http://example.com</wp:base_blog_url>

	<wp:author>
		<wp:author_id>1</wp:author_id>
		<wp:author_login><![CDATA[boshag]]></wp:author_login>
		<wp:author_email><![CDATA[boshag@example.com]]></wp:author_email>
		<wp:author_display_name><![CDATA[boshag]]></wp:author_display_name>
		<wp:author_first_name><![CDATA[]]></wp:author_first_name>
		<wp:author_last_name><![CDATA[]]></wp:author_last_name>
	</wp:author>

	<wp:category>
		<wp:term_id>12</wp:term_id>
		<wp:category_nicename><![CDATA[general]]></wp:category_nicename>
		<wp:category_parent><![CDATA[]]></wp:category_parent>
		<wp:cat_name><![CDATA[General]]></wp:cat_name>
		<wp:category_description><![CDATA[General topics.]]></wp:category_description>
	</wp:category>

	<wp:tag>
		<wp:term_id>34</wp:term_id>
		<wp:tag_slug><![CDATA[uncategorized]]></wp:tag_slug>
		<wp:tag_name><![CDATA[Uncategorized]]></wp:tag_name>
	</wp:tag>

	<wp:term>
		<wp:term_id><![CDATA[12]]></wp:term_id>
		<wp:term_taxonomy><![CDATA[category]]></wp:term_taxonomy>
		<wp:term_slug><![CDATA[general]]></wp:term_slug>
		<wp:term_parent><![CDATA[]]></wp:term_parent>
		<wp:term_name><![CDATA[General]]></wp:term_name>
	</wp:term>

	<wp:term>
		<wp:term_id><![CDATA[34]]></wp:term_id>
		<wp:term_taxonomy><![CDATA[post_tag]]></wp:term_taxonomy>
		<wp:term_slug><![CDATA[uncategorized]]></wp:term_slug>
		<wp:term_parent><![CDATA[]]></wp:term_parent>
		<wp:term_name><![CDATA[Uncategorized]]></wp:term_name>
	</wp:term>

	<generator>https://wordpress.org/?v=4.8.2</generator>

	<item>
		<title>Sample Page</title>
		<link>http://example.com/sample-page</link>
		<pubDate>Sat, 02 Jan 2016 22:58:37 +0000</pubDate>
		<dc:creator><![CDATA[boshag]]></dc:creator>
		<guid isPermaLink="false">http://159.203.117.128/?page_id=2</guid>
		<description></description>
		<content:encoded><![CDATA[This is an example page.]]></content:encoded>
		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
		<wp:post_id>2</wp:post_id>
		<wp:post_date><![CDATA[2016-01-02 22:58:37]]></wp:post_date>
		<wp:post_date_gmt><![CDATA[2016-01-02 22:58:37]]></wp:post_date_gmt>
		<wp:comment_status><![CDATA[closed]]></wp:comment_status>
		<wp:ping_status><![CDATA[open]]></wp:ping_status>
		<wp:post_name><![CDATA[sample-page]]></wp:post_name>
		<wp:status><![CDATA[publish]]></wp:status>
		<wp:post_parent>0</wp:post_parent>
		<wp:menu_order>0</wp:menu_order>
		<wp:post_type><![CDATA[page]]></wp:post_type>
		<wp:post_password><![CDATA[]]></wp:post_password>
		<wp:is_sticky>0</wp:is_sticky>
		<wp:postmeta>
			<wp:meta_key><![CDATA[_wp_page_template]]></wp:meta_key>
			<wp:meta_value><![CDATA[default]]></wp:meta_value>
		</wp:postmeta>
	</item>

	<item>
		<title>Sample Post</title>
		<link>http://example.com/archives/6</link>
		<pubDate>Sun, 15 Aug 2004 22:55:21 +0000</pubDate>
		<dc:creator><![CDATA[boshag]]></dc:creator>
		<guid isPermaLink="false">http://example.com/blog/?p=2</guid>
		<description></description>
		<content:encoded><![CDATA[Sample post.]]></content:encoded>
		<excerpt:encoded><![CDATA[]]></excerpt:encoded>
		<wp:post_id>6</wp:post_id>
		<wp:post_date><![CDATA[2004-08-15 18:55:21]]></wp:post_date>
		<wp:post_date_gmt><![CDATA[2004-08-15 22:55:21]]></wp:post_date_gmt>
		<wp:comment_status><![CDATA[open]]></wp:comment_status>
		<wp:ping_status><![CDATA[open]]></wp:ping_status>
		<wp:post_name><![CDATA[sample-post]]></wp:post_name>
		<wp:status><![CDATA[publish]]></wp:status>
		<wp:post_parent>0</wp:post_parent>
		<wp:menu_order>0</wp:menu_order>
		<wp:post_type><![CDATA[post]]></wp:post_type>
		<wp:post_password><![CDATA[]]></wp:post_password>
		<wp:is_sticky>0</wp:is_sticky>
		<category domain="category" nicename="general"><![CDATA[General]]></category>
	</item>
</channel>
</rss>


code:
<?php
declare(strict_types=1);

namespace Argo\App;

class OpenFolderTest extends \Argo\App\TestCase
{
    public function testError()
    {
        $folder = 'no-such-folder';
        $payload = $this->invoke($folder);
        $this->assertError($payload, "no-such-folder' not found.");
    }

    public function testProcessing()
    {
        $this->setUpArgo();

        $folder = '_trash';
        $payload = $this->invoke($folder);
        $this->assertAccepted($payload);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Site;

class AddSiteTest extends \Argo\App\TestCase
{
    public function testInitialSetup()
    {
        $payload = $this->invoke();
        $this->assertFound($payload);
        $this->assertEquals($payload->getResult(), [
            'name' => '',
            'title' => '',
            'tagline' => '',
            'author' => getenv('USER') ?: '',
            'url' => '',
        ]);
    }

    public function testInvalidInput()
    {
        $input = ['name' => '   '];
        $payload = $this->invoke($input);
        $this->assertInvalid($payload, 'The folder name may not be blank.');

        $input['name'] = '!!!';
        $payload = $this->invoke($input);
        $this->assertInvalid($payload, 'The folder name may use only a-z, 0-9, and dashes.');

        mkdir($this->system->sitesDir() . '/argo-test-prior');

        $input['name'] = 'argo-test-prior';
        $payload = $this->invoke($input);
        $this->assertInvalid($payload, "A folder with the name 'argo-test-prior' already exists.");

        $input['name'] = 'argo-test';

        $input['title'] = '   ';
        $payload = $this->invoke($input);
        $this->assertInvalid($payload, 'The blog title may not be blank.');

        $input['title'] = 'Title Title';
        $input['author'] = '   ';

        $payload = $this->invoke($input);
        $this->assertInvalid($payload, 'The author username may not be blank.');

        $input['author'] = '!!!';
        $payload = $this->invoke($input);
        $this->assertInvalid($payload, 'The author username may use only a-z, 0-9, and dashes.');

        $input['author'] = 'boshag';
        $input['url'] = '   ';
        $payload = $this->invoke($input);
        $this->assertInvalid($payload, 'The blog URL may not be blank.');

        $input['url'] = '/path/bar';
        $payload = $this->invoke($input);
        $this->assertInvalid($payload, 'The blog URL does not look right.');
    }

    public function testValidInput() : void
    {
        $this->assertFalse($this->storage->exists('_config/general.json'));

        $input = [
            'name' => 'argo-test',
            'title' => 'Title Title',
            'tagline' => 'Tagline tagline.',
            'author' => 'boshag',
            'url' => 'http://example.com'
        ];

        $payload = $this->invoke($input);

        $this->assertSuccess($payload);

        $ids = [
            '_argo/admin.json',
            // '_argo/blogroll.json',
            '_argo/general.json',
            // '_argo/menu.json',
            // '_argo/sync.json',
            // '_argo/theme.json',
            // '_argo/blogroll.json',
            // '_theme/',
            // '_trash/',
            // 'post/0001/02/03/sample-post/argo.json',
            // 'post/0001/02/03/sample-post/index.html',
            // 'post/0001/02/03/sample-post/index.json',
            // 'posts/month/0001/02/index.html',
            // 'posts/month/0001/02/index.json',
            // 'posts/months/index.html',
            // 'posts/months/index.json',
            // 'posts/months/index.shtml',
            // 'tag/general/atom.xml',
            // 'tag/general/argo.json',
            // 'tag/general/index.html',
            // 'tag/general/index.json',
            // 'tags/index.html',
            // 'tags/index.json',
            // 'tags/index.shtml',
            // 'theme/default/',
            // 'atom.xml',
            // 'blogroll.shtml',
            // 'index.html',
            // 'menu.shtml',
        ];

        foreach ($ids as $id) {
            $this->assertTrue($this->storage->exists($id), "$id does not exist");
        }
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Site;

use Argo\Infra\System;

class BuildSiteTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testSuccess() : void
    {
        $payload = $this->invoke();
        $this->assertProcessing($payload);
        // @todo assert the callable
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App\Site;

use Argo\Infra\System;

class SyncSiteTest extends \Argo\App\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->setUpArgo();
    }

    public function testInvalid() : void
    {
        $this->markTestIncomplete('need a way to test invalid sync config');

        $sync = $this->config->sync;

        unset($sync->type);
        $payload = $this->invoke();
        $this->assertInvalid($payload, "No sync type specified.");

        $sync->type = null;
        $payload = $this->invoke();
        $this->assertInvalid($payload, "No sync type specified.");

        $sync->type = '   ';
        $payload = $this->invoke();
        $this->assertInvalid($payload, "No sync type specified.");

        $sync->type = 'nonesuch';
        $payload = $this->invoke();
        $this->assertInvalid($payload, "Sync type 'nonesuch' not recognized.");
    }

    public function testProcessing_git() : void
    {
        $this->config->sync->type = 'git';

        $payload = $this->invoke();
        $this->assertProcessing($payload);

        // @todo: review $result->command value?
    }

    public function testSuccess_rsync() : void
    {
        $this->config->sync->type = 'rsync';
        $this->config->sync->host = 'example.com';
        $this->config->sync->user = 'boshag';
        $this->config->sync->path = '/var/www/html';

        $payload = $this->invoke();
        $this->assertProcessing($payload);

        // @todo: review $result->command value?
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\App;

use Argo\Domain\FakeDateTime;
use Argo\Domain\Json;
use Argo\Infra\BuildFactory;
use Argo\Infra\Preflight;
use Argo\Infra\System;

abstract class TestCase extends \Argo\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->dateTime->setTimezone('UTC');
    }

    protected function setUpArgo() : void
    {
        /* fake a previous AddSite interaction */
        $docroot = $this->system->docroot();

        mkdir("{$docroot}/_argo", 0755, true);

        $this->config->general = $this->configGateway->newValues('_argo/general', (object) [
            'title' => 'Argo Blog Title',
            'tagline' => 'Argo Blog Tagline',
            'author' => 'boshag',
            'url' => 'http://example.com',
            'theme' => 'bootstrap4'
        ]);

        $this->configGateway->saveValues($this->config->general);

        $this->config->admin = $this->configGateway->newValues('_argo/admin', (object) [
            'initialize' => true,
        ]);

        $this->configGateway->saveValues($this->config->admin);

        file_put_contents(
            $this->system->supportDir() . '/docroot.php',
            "<?php return '{$docroot}';"
        );

        /* preflight will create the needed structure */
        $preflight = $this->container->get(Preflight::CLASS);
        $preflight('/');
    }

    protected function invoke(...$args) : Payload
    {
        $class = substr(get_class($this), 0, -4);
        $useCase = $this->container->get($class);
        $payload = $useCase(...$args);
        return $payload;
    }

    protected function assertCreated(Payload $actual) : void
    {
        $this->assertStatus(Status::CREATED, $actual);
    }

    protected function assertUpdated(Payload $actual) : void
    {
        $this->assertStatus(Status::UPDATED, $actual);
    }

    protected function assertDeleted(Payload $actual) : void
    {
        $this->assertStatus(Status::DELETED, $actual);
    }

    protected function assertNotFound(Payload $actual) : void
    {
        $this->assertStatus(Status::NOT_FOUND, $actual);
    }

    protected function assertFound(Payload $actual) : void
    {
        $this->assertStatus(Status::FOUND, $actual);
    }

    protected function assertInvalid(Payload $actual, string $message = null) : void
    {
        $this->assertStatus(Status::INVALID, $actual);
        if ($message !== null) {
            $this->assertStringContainsString($message, $actual->getResult()['invalid']);
        }
    }

    protected function assertError(Payload $actual, string $message = null) : void
    {
        $this->assertStatus(Status::ERROR, $actual);
        if ($message !== null) {
            $this->assertStringContainsString($message, $actual->getResult()['error']);
        }
    }

    protected function assertProcessing(Payload $actual) : void
    {
        $this->assertStatus(Status::PROCESSING, $actual);
    }

    protected function assertAccepted(Payload $actual) : void
    {
        $this->assertStatus(Status::ACCEPTED, $actual);
    }

    protected function assertSuccess(Payload $actual) : void
    {
        $this->assertStatus(Status::SUCCESS, $actual);
    }

    protected function assertStatus($expect, Payload $actual)
    {
        $this->assertSame(
            $expect,
            $actual->getStatus(),
            "Expected {$expect}, got " .  print_r($actual, true)
        );
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Config;

use Argo\Domain\Config\Values\Fake;

class ConfigGatewayTest extends \Argo\Domain\TestCase
{
    protected $configGateway;

    protected function setUp() : void
    {
        parent::setUp();
        $this->configGateway = $this->container->get(ConfigGateway::CLASS);
    }

    public function test() : void
    {
        $fake = $this->configGateway->newValues('_argo/fake', (object) ['foo' => 'bar']);
        $this->configGateway->saveValues($fake);

        $config = $this->configGateway->getConfig();
        $this->assertEquals($fake, $config->fake);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Config;

class ConfigTest extends \Argo\Domain\TestCase
{
    public function test__magic()
    {
        $fake = new Values('_argo/fake');

        $config = new Config([
            'fake' => $fake,
        ]);

        $this->assertSame($fake, $config->fake);
        $this->assertTrue(isset($config->fake));
        unset($config->fake);
        $this->assertFalse(isset($config->fake));
    }

    public function testAssertValues()
    {
        $this->expectDomainException('Not an instance of Argo\Domain\Config\Values');
        $config = new Config([
            'fake' => null,
        ]);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Config\Values;

class Fake extends Values
{
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Config;

class ValuesTest extends \Argo\Domain\TestCase
{
    protected function newFake(array $data)
    {
        return new Values('_argo/fake', (object) $data);
    }

    public function test__magic()
    {
        $fake = $this->newFake(['foo' => 'bar']);
        $this->assertSame('bar', $fake->foo);
        $fake->baz = 'dib';
        $this->assertSame('dib', $fake->baz);
        unset($fake->foo);
        $this->assertFalse(isset($fake->foo));
    }

    public function testGetText()
    {
        $expect = ['foo' => 'bar'];
        $fake = $this->newFake($expect);
        $actual = $fake->getText();
        $this->assertJsonEquals($expect, $actual);
    }

    public function testGetData()
    {
        $expect = ['foo' => 'bar'];
        $fake = $this->newFake($expect);
        $actual = $fake->getData();
        $this->assertEquals((object) $expect, $actual);
    }

    public function testGetIterator()
    {
        $expect = [
            'foo' => 'bar',
            'baz' => 'dib',
            'zim' => 'gir',
        ];

        $fake = $this->newFake($expect);
        foreach ($fake as $key => $val) {
            $this->assertSame($expect[$key], $val);
        }
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content;

use Argo\Domain\Content\Draft\DraftRepository;
use Argo\Domain\Content\Page\PageRepository;
use Argo\Domain\Content\Tag\TagRepository;
use Argo\Domain\Content\Post\PostRepository;
use Argo\Domain\Storage;
use RuntimeException;

class ContentLocatorTest extends \Argo\Domain\TestCase
{
    /**
     * @dataProvider provideInstance
     */
    public function testInstance($name, $class) : void
    {
        $this->assertInstanceOf($class, $this->content->$name);
    }

    public function provideInstance()
    {
        return [
            ['drafts', DraftRepository::CLASS],
            ['tags', TagRepository::CLASS],
            ['pages', PageRepository::CLASS],
            ['posts', PostRepository::CLASS],
        ];
    }

    public function testInstanceFailure()
    {
        $this->expectDomainException("No such repository: nonesuch");
        $this->content->nonesuch;
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content\Draft;

use Argo\Domain\DateTime;
use Argo\Domain\Content\Post\Post;

class DraftRepositoryTest extends \Argo\Domain\TestCase
{
    public function testGetItems() : void
    {
        for ($i = 10; $i < 20; $i++) {
            $id = Draft::absId("00010203T0405{$i}");
            $data = ['title' => "Title {$i}"];
            $body = "Body {$i}";
            $draft = new Draft($id, $data);
            $this->content->drafts->save($draft, $body);
        }

        $actual = $this->content->drafts->getItems();
        $this->assertCount(10, $actual);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content\Draft;

class DraftTest extends \Argo\Domain\TestCase
{
    public function testAssertId() : void
    {
        Draft::assertId('_draft/00010101T123456');
        $this->assertTrue(true);
    }

    public function testAssertIdException() : void
    {
        $this->expectInvalidData('Draft ID is not valid');
        Draft::assertId('foo');
    }

    public function testAbsId() : void
    {
        $relId = '00010101T123456';
        $expect = '_draft/00010101T123456';
        $actual = Draft::absId($relId);
        $this->assertSame($expect, $actual);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content;

use Argo\Domain\Exception;

class FakeItem extends Item
{
    public static function assertId(string $id) : void
    {
        parent::assertId($id);
        $regex = '/^fake\/[a-z0-9-]+$/';
        if (! preg_match($regex, $id)) {
            throw new Exception\InvalidData("FakeItem ID is not valid: {$id}");
        }
    }

    static public function absId(string $relId) : string
    {
        return "fake/{$relId}";
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content;

class FakeItemRepository extends ItemRepository
{
    protected function getGlob() : string
    {
        return 'fake/*';
    }

    protected function glob() : array
    {
        $items = parent::glob();
        natsort($items);
        return array_values($items);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content;

use Argo\Domain\Content\Page\Page;

class FakeItemRepositoryTest extends \Argo\Domain\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();
        $this->content->fakes = new FakeItemRepository(
            $this->dateTime,
            $this->storage,
            $this->config,
            $this->content
        );
    }

    public function testSaveAndGet() : void
    {
        $id = 'fake/foo';
        $data = ['title' => 'Title Title'];
        $body = 'Body body body';
        $fake = new FakeItem($id, $data);

        $this->content->fakes->save($fake, $body);

        $actual = $this->storage->exists('/fake/foo/argo.json');
        $this->assertTrue($actual);

        $actual = $this->content->fakes->getItem('foo');
        $this->assertEquals($fake, $actual);

        $actual = $this->content->fakes->getItem('nonesuch');
        $this->assertNull($actual);
    }

    public function testSaveWrongType() : void
    {
        $id = 'foo';
        $data = ['title' => 'Title Title'];
        $body = 'Body body body';
        $page = new Page($id, $data);

        $this->expectDomainException("Wrong type:");
        $this->content->fakes->save($page, $body);
    }

    public function testListCounts() : void
    {
        for ($i = 1; $i <= 50; $i++) {
            $id = "fake/foo-{$i}";
            $data = ['title' => "Title {$i}"];
            $body = "Body {$i}";
            $fake = new FakeItem($id, $data);
            $this->content->fakes->save($fake, $body);
        }

        // predicated on config->general->perPage = 10
        list($itemCount, $pageCount) = $this->content->fakes->listCounts();
        $this->assertSame(50, $itemCount);
        $this->assertSame(5, $pageCount);
    }

    public function testGetItems() : void
    {
        for ($i = 1; $i <= 50; $i++) {
            $id = "fake/foo-{$i}";
            $data = ['title' => "Title {$i}"];
            $body = "Body {$i}";
            $fake = new FakeItem($id, $data);
            $this->content->fakes->save($fake, $body);
        }

        $actual = $this->content->fakes->getItems();
        $this->assertCount(50, $actual);

        $actual = $this->content->fakes->getItems(3);
        $this->assertCount(10, $actual);
        $this->assertSame('fake/foo-21', reset($actual)->id);
        $this->assertSame('fake/foo-30', end($actual)->id);
    }

    public function testTrash() : void
    {
        $id = 'fake/foo';
        $data = ['title' => 'Title Title'];
        $body = 'Body body body';
        $fake = new FakeItem($id, $data);

        $this->content->fakes->save($fake, $body);

        $this->content->fakes->trash($fake);
        $this->assertFalse($this->storage->exists('/fake/foo'));
        // $this->assertTrue($this->storage->exists('/_trash/fake/foo'));
    }

    public function testTrashWrongType()
    {
        $id = 'foo';
        $data = ['title' => 'Title Title'];
        $page = new Page($id, $data);

        $this->expectDomainException("Wrong type:");
        $this->content->fakes->trash($page);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content;

use Argo\Domain\Json;

class FakeItemTest extends \Argo\Domain\TestCase
{
    public function testAssertId() : void
    {
        $this->expectNotToPerformAssertions();
        FakeItem::assertId('fake/foo');
    }

    /**
     * @dataProvider provideAssertIdExceptions
     */
    public function testAssertIdExceptions(string $id, string $message) : void
    {
        $this->expectInvalidData($message);
        FakeItem::assertId($id);
    }

    public function provideAssertIdExceptions() : array
    {
        return [
            ['', 'FakeItem ID cannot be blank'],
            ['foo_bar/baz.dib', 'FakeItem ID has invalid characters; use only a-z, 0-9, and dashes'],
            ['/foo/bar/', 'FakeItem ID cannot begin or end with slashes'],
            ['foo//bar', 'FakeItem ID cannot have contiguous slashes'],
            ['fake/foo/bar', 'FakeItem ID is not valid'],
        ];
    }

    public function testNew() : void
    {
        $data = ['title' => 'Title Title'];
        $fake = new FakeItem('fake/foo', $data);

        $this->assertSame('fake/foo', $fake->id);
        $this->assertSame('foo', $fake->relId);
        $this->assertSame('/fake/foo/', $fake->href);
        $expect = (object) ($data + [
            'author' => null,
            'created' => null,
            'updated' => [],
            'markup' => 'markdown',
            'tags' => [],
        ]);
        $this->assertEquals($expect, $fake->data);
    }

    protected function text(array $data, string $body) : string
    {
        return "```\n"
            . Json::encode($data)
            . "\n```\n\n"
            . trim($body)
            . "\n";
    }

    public function testNormalize() : void
    {
        $this->assertSame('foo-bar-baz-dib-gir', FakeItem::normalize("Foo Bar. Baz, dib GIR"));
    }

    public function testTitleize() : void
    {
        $this->assertSame('Foo Bar Baz Dib Gir', FakeItem::titleize('foo-bar-baz-dib-gir'));
    }

    public function test__get() : void
    {
        $fake = new FakeItem(
            'fake/foo',
            ['title' => 'Title Title'],
            'Body body body'
        );

        $this->assertSame('fake/foo', $fake->id);
        $this->assertSame('Title Title', $fake->title);

        $this->expectDomainException('Cannot get $nonesuch on content object.');
        $fake->nonesuch;
    }

    public function test__set() : void
    {
        $fake = new FakeItem(
            'fake/foo',
            ['title' => 'Title Title'],
            'Body body body'
        );

        $this->expectDomainException('Cannot set $id on content object.');
        $fake->id = null;
    }

    public function test__isset() : void
    {
        $fake = new FakeItem(
            'fake/foo',
            ['title' => 'Title Title'],
            'Body body body'
        );

        $this->assertTrue(isset($fake->title));
        $this->assertFalse(isset($fake->author));
        $this->assertFalse(isset($fake->nonesuch));
    }

    public function test__unset() : void
    {
        $fake = new FakeItem(
            'fake/foo',
            ['title' => 'Title Title'],
            'Body body body'
        );

        $this->expectDomainException('Cannot unset $id on content object.');
        unset($fake->id);
    }

    public function testFill() : void
    {
        $fake = new FakeItem('fake/foo');
        $this->assertSame('fake/foo', $fake->id);
        $this->assertSame('foo', $fake->relId);
        $this->assertSame('/fake/foo/', $fake->href);
        $data = [
            'title' => null,
            'author' => null,
            'created' => null,
            'updated' => [],
            'markup' => 'markdown',
            'tags' => [],
        ];
        $this->assertEquals((object) $data, $fake->data);

        $data = [
            'title' => 'Title Title',
            'author' => 'boshag',
            'tags' => 'foo, bar, baz'
        ];
        $fake->fill($data);
        $this->assertSame('Title Title', $fake->title);
        $this->assertSame('boshag', $fake->author);
        $this->assertEquals(['foo', 'bar', 'baz'], $fake->tags);
    }

    public function testFillWithInvalidAuthor() : void
    {
        $fake = new FakeItem('fake/foo');
        $data = [
            'author' => '!!!',
        ];
        $body = 'Body body body';

        $this->expectInvalidData('Author has invalid characters; use only a-z, 0-9, and dashes.');
        $fake->fill($data, $body);
    }

    public function testFillWithInvalidTag() : void
    {
        $fake = new FakeItem('fake/foo');
        $data = [
            'tags' => 'foo, bar baz ',
        ];

        $this->expectInvalidData("Tag 'bar baz' has invalid characters; use only a-z, 0-9, and dashes.");
        $fake->fill($data);
    }

    public function testJsonSerialize() : void
    {
        $data = ['title' => 'Title Title'];
        $fake = new FakeItem('fake/foo', $data);

        $expect = [
            'href' => '/fake/foo/',
            'relId' => 'foo',
            'title' => 'Title Title',
            'author' => null,
            'tags' => [],
            'created' => null,
            'updated' => [],
            'markup' => 'markdown',
        ];
        $actual = json_decode(json_encode($fake), true);
        $this->assertSame($expect, $actual);
    }

    public function testToJson() : void
    {
        $data = ['title' => 'Title Title'];
        $body = "Body body body";
        $fake = new FakeItem('fake/foo', $data, $body);

        $expect = [
            'href' => '/fake/foo/',
            'relId' => 'foo',
            'title' => 'Title Title',
            'author' => null,
            'tags' => [],
            'created' => null,
            'updated' => [],
            'markup' => 'markdown',
            'foo' => 'bar',
        ];

        $actual = json_decode($fake->toJson(['foo' => 'bar']), true);
        $this->assertSame($expect, $actual);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content;

use Argo\Domain\Content\Post\Post;
use Argo\Domain\Json;

class MonthTest extends \Argo\Domain\TestCase
{
    public function test() : void
    {
        $prev = null;
        $next = null;
        $months = [];
        $y = '2000';
        for ($m = 10; $m <= 12; $m++) {
            $ym = "{$y}-{$m}";
            $curr = new Month($ym, $this->dateTime->month($ym));
            $curr->setPrev($prev);
            if ($prev) {
                $prev->setNext($curr);
            }
            $prev = $curr;
            $months[$ym] = $curr;
        }

        $this->assertCount(3, $months);

        $this->assertSame(null,               $months['2000-10']->prev);
        $this->assertSame($months['2000-11'], $months['2000-10']->next);

        $this->assertSame($months['2000-10'], $months['2000-11']->prev);
        $this->assertSame($months['2000-12'], $months['2000-11']->next);

        $this->assertSame($months['2000-11'], $months['2000-12']->prev);
        $this->assertSame(null,               $months['2000-12']->next);

        $month = $months['2000-11'];

        $post = new Post('post/2000/11/22/foo', [], 'Body body body');
        $month->attachPost($post);
        $this->assertSame([$post], $month->posts);

        $expect = [
            'href' => '/posts/month/2000/11/',
            'title' => 'November 2000',
            'count' => 1
        ];
        $this->assertJsonEquals($expect, Json::encode($month));

        $expect += ['foo' => 'bar'];
        $this->assertJsonEquals($expect, $month->toJson(['foo' => 'bar']));
    }

    public function testGetAllFromPosts() : void
    {
        $posts = [];
        $y = '2000';

        for ($m = 10; $m <= 12; $m++) {
            for ($d = 20; $d < 30; $d++) {
                $id = Post::absId("{$y}/{$m}/{$d}/title-{$d}");
                $data = [
                    'title' => "Title {$d}",
                    'tags' => [
                        'foo',
                    ],
                ];
                $posts[] = new Post($id, $data);
            }
        }

        $actual = Month::getAllFromPosts($posts, $this->dateTime);
        $this->assertCount(3, $actual);

        $this->assertCount(10, $actual['2000-10']->posts);
        $this->assertCount(10, $actual['2000-11']->posts);
        $this->assertCount(10, $actual['2000-12']->posts);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content\Page;

class PageRepositoryTest extends \Argo\Domain\TestCase
{
    public function testGetItems() : void
    {
        for ($i = 0; $i < 10; $i++) {
            $id = "page-{$i}";
            $data = ['title' => "Title {$i}"];
            $body = "Body {$i}";
            $page = new Page($id, $data);
            $this->content->pages->save($page, $body);
        }

        $actual = $this->content->pages->getItems();
        $this->assertCount(10, $actual);

        $this->assertSame('page-0', reset($actual)->id);
        $this->assertSame('page-9', end($actual)->id);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content\Page;

class PageTest extends \Argo\Domain\TestCase
{
    public function testAssertId() : void
    {
        Page::assertId('foo-bar');
        Page::assertId('foo/bar');
        $this->assertTrue(true);
    }

    /**
     * @dataProvider provideAssertIdException
     */
    public function testAssertIdException(string $id, string $message) : void
    {
        $this->expectInvalidData($message);
        Page::assertId($id);
    }

    public function provideAssertIdException()
    {
        return [
            ['tag', 'Page ID disallowed'],
            ['tags', 'Page ID disallowed'],
            ['post', 'Page ID disallowed'],
            ['posts', 'Page ID disallowed'],
            ['theme', 'Page ID disallowed'],
        ];
    }

    public function testAbsId() : void
    {
        $relId = 'foo';
        $expect = 'foo';
        $actual = Page::absId($relId);
        $this->assertSame($expect, $actual);
    }

    public function testGetItems() : void
    {
        for ($i = 0; $i < 10; $i++) {
            $id = "page-{$i}";
            $data = ['title' => "Title {$i}"];
            $body = "Body {$i}";
            $page = new Page($id, $data);
            $this->content->pages->save($page, $body);
        }

        $actual = $this->content->pages->getItems();
        $this->assertCount(10, $actual);

        $this->assertSame('page-0', reset($actual)->id);
        $this->assertSame('page-9', end($actual)->id);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content\Post;

use Argo\Domain\Content\Draft\Draft;

class PostTest extends \Argo\Domain\TestCase
{
    public function testAssertId() : void
    {
        Post::assertId('post/0001/01/01/foo-bar-1');
        $this->assertTrue(true);
    }

    public function testAssertIdException() : void
    {
        $this->expectInvalidData('Post ID is not valid');
        Post::assertId('foo');
    }

    public function testAbsId() : void
    {
        $relId = '0001/01/01/foo-bar-1';
        $expect = 'post/0001/01/01/foo-bar-1';
        $actual = Post::absId($relId);
        $this->assertSame($expect, $actual);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content\Tag;

use Argo\Domain\Content\Post\Post;

class TagRepositoryTest extends \Argo\Domain\TestCase
{
    public function testGetItems() : void
    {
        for ($i = 0; $i < 10; $i++) {
            $id = Tag::absId("foo-{$i}");
            $data = ['title' => "Foo {$i}"];
            $tag = new Tag($id, $data);
            $this->content->tags->save($tag, '');
        }

        $actual = $this->content->tags->getItems();
        $this->assertCount(10, $actual);
    }

    public function testAdhocs() : void
    {
        $actual = $this->content->tags->getItems();
        $this->assertEmpty($actual);

        $relIds = ['foo', 'bar', 'baz'];
        $this->content->tags->adhocs($relIds);

        $actual = $this->content->tags->getItems();
        $this->assertCount(3, $actual);

        $this->assertSame('Foo', $actual['tag/foo']->title);
        $this->assertSame('Bar', $actual['tag/bar']->title);
        $this->assertSame('Baz', $actual['tag/baz']->title);
    }

    public function testGetAllFromPosts() : void
    {
        $tags = ['foo', 'bar', 'baz'];
        for ($i = 0; $i < 9; $i++) {
            $id = Post::absId("0001/02/03/title-{$i}");
            $data = [
                'title' => "Title {$i}",
                'tags' => [
                    $tags[$i % 3],
                ],
            ];
            $body = "Body body {$i}";
            $post = new Post($id, $data);
            $this->content->posts->save($post, $body);
        }

        $posts = $this->content->posts->getItems();
        $actual = $this->content->tags->getAllFromPosts($posts);
        $this->assertCount(3, $actual);

        $this->assertSame('Foo', $actual['tag/foo']->title);
        $this->assertSame('Bar', $actual['tag/bar']->title);
        $this->assertSame('Baz', $actual['tag/baz']->title);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain\Content\Tag;

use Argo\Domain\Content\Post\Post;

class TagTest extends \Argo\Domain\TestCase
{
    public function testAssertId() : void
    {
        Tag::assertId('tag/foo-bar');
        $this->assertTrue(true);
    }

    public function testAssertIdException() : void
    {
        $this->expectInvalidData('Tag ID has invalid characters; use only a-z, 0-9, and dashes');
        Tag::assertId('foo space bar');
    }

    public function testAbsId() : void
    {
        $relId = 'foo-bar';
        $expect = 'tag/foo-bar';
        $actual = Tag::absId($relId);
        $this->assertSame($expect, $actual);
    }

    public function testAttachPost() : void
    {
        $tag = new Tag(
            Tag::absId('foo-bar'),
            ['title' => 'Foo Bar'],
            ''
        );

        $post = new Post(
            Post::absId('0001/02/03/title-title'),
            ['title' => 'Title Title'],
            'Body body body'
        );

        $tag->attachPost($post);

        $this->assertCount(1, $tag->posts);
    }

    public function testSetPrevAndNext() : void
    {
        $foo = new Tag(
            Tag::absId('foo'),
            ['title' => 'Foo Tag'],
            ''
        );

        $bar = new Tag(
            Tag::absId('bar'),
            ['title' => 'Bar Tag'],
            ''
        );

        $baz = new Tag(
            Tag::absId('baz'),
            ['title' => 'Baz Tag'],
            ''
        );

        $foo->setNext($bar);

        $bar->setPrev($foo);
        $bar->setNext($baz);

        $baz->setPrev($foo);

        $this->assertNull($foo->prev);
        $this->assertSame($bar, $foo->next);

        $this->assertSame($foo, $bar->prev);
        $this->assertSame($baz, $bar->next);

        $this->assertSame($foo, $baz->prev);
        $this->assertNull($baz->next);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain;

class DateTimeTest extends \Argo\Domain\TestCase
{
    protected $dateTime;

    protected function setUp() : void
    {
        $this->dateTime = new DateTime();
    }

    public function testTimezone() : void
    {
        $this->assertSame('UTC', $this->dateTime->getTimezone());
        $this->dateTime->setTimezone('America/Chicago');
        $this->assertSame('America/Chicago', $this->dateTime->getTimezone());
    }

    public function testUtc() : void
    {
        $utc = $this->dateTime->utc();
        $this->assertRegexp('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC$/', $utc);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain;

use DateTimeImmutable;
use DateTimeZone;

class FakeDateTime extends DateTime
{
    public $now = 'now';

    protected function now() : string
    {
        return $this->now;
    }

    public function modify(string $modifier) : string
    {
        $date = new DateTimeImmutable($this->now(), new DateTimeZone('UTC'));
        $date = $date->modify($modifier);
        $this->now = $date->format('Y-m-d H:i:s e');
        return $this->now;
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain;

class JsonTest extends \Argo\Domain\TestCase
{
    public function testRecode()
    {
        $expect = ['foo' => 'bar'];

        $actual = Json::recode($expect);
        $this->assertTrue(is_object($actual));
        $this->assertSame($expect, (array) $actual);

        $actual = Json::recode($actual, true);
        $this->assertTrue(is_array($actual));
        $this->assertSame($expect, $actual);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Domain;

use Argo\Domain\Config\Values;

abstract class TestCase extends \Argo\TestCase
{
    protected function setUp() : void
    {
        parent::setUp();

        $this->config->general = new Values(
            '_argo/general',
            (object) [
                'perPage' => 10,
                'author' => 'boshag',
            ]
        );
    }

    protected function expectDomainException(string $message) : void
    {
        $this->expectException(Exception::CLASS);
        $this->expectExceptionMessage($message);
    }

    protected function expectInvalidData(string $message) : void
    {
        $this->expectException(Exception\InvalidData::CLASS);
        $this->expectExceptionMessage($message);
    }

    protected function expectInvalidJson() : void
    {
        $this->expectException(Exception\InvalidJson::CLASS);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Infra;

use Argo\Domain\Log;
use Psr\Log\AbstractLogger;
use Psr\Log\LogLevel;

class FakeLog extends Stdlog
{
    public function __construct()
    {
        $this->stdout = fopen('php://memory', 'w');
        $this->stderr = fopen('php://memory', 'w');
    }

    public function getStdout()
    {
        return $this->stdout;
    }

    public function getStderr()
    {
        return $this->stderr;
    }

    public function echo($message, array $context = [])
    {
        $handle = $this->stdout;
        fwrite($handle, $this->interpolate($message, $context));
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Infra;

class FakeServer extends Server
{
    public function start() : void
    {
    }

    public function stop(string $docroot = null) : void
    {
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Infra;

use Argo\Domain\Log;

class FakeSystem extends System
{
    public $timezone = 'UTC';

    public $command;

    public function timezone() : string
    {
        return $this->timezone;
    }

    public function exec(string $cmd, Log $log = null, string $level = null) : void
    {
        $this->cmd = $cmd;
    }

    public function homeDir() : string
    {
        return dirname(__DIR__) . '/tmp';
    }

    public function docroot() : string
    {
        return $this->sitesDir() . '/argo-test';
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Infra;

use Argo\Domain\DateTime;
use RuntimeException;

class FsioTest extends \Argo\TestCase
{
    protected $root;

    protected function setUp() : void
    {
        parent::setUp();
        $this->root = dirname(__DIR__) . '/tmp';
        $this->fsio = new Fsio(new System(), new DateTime(), $this->root);
    }

    public function testPath() : void
    {
        $expect = "{$this->root}/foo/bar";
        $actual = $this->fsio->path('foo/bar');
        $this->assertSame($expect, $actual);

        $this->expectException(RuntimeException::CLASS);
        $this->expectExceptionMessage("Double-dots not allowed in IDs");
        $this->fsio->path('foo/../../bar');
    }

    public function testReadWrite() : void
    {
        $id = 'foo/bar';
        $expect = uniqid() . "\n";
        $this->fsio->write("{$id}/argo.json", $expect);
        $actual = $this->fsio->read("{$id}/argo.json");
        $this->assertSame($expect, $actual);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo\Infra;

use Argo\Domain\DateTime;
use RuntimeException;

class SystemTest extends \Argo\TestCase
{
    protected $home;

    protected $system;

    /**
     * @dataProvider provide
     */
    public function test(string $os, string $method, string $expect)
    {
        $system = new FakeSystem($os);
        $actual = $system->$method();
        $this->assertSame($expect, $actual);
    }

    public function provide() : array
    {
        $home = dirname(__DIR__) . '/tmp';
        return [
            ['darwin', 'sitesDir', "{$home}/Sites"],
            ['darwin', 'supportDir', "{$home}/Library/Application Support/Argo"],
            ['linux', 'sitesDir', "{$home}/Sites"],
            ['linux', 'supportDir', "{$home}/.config/Argo"],
        ];
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo;

use Argo\Domain\Config\Config;
use Argo\Domain\Config\ConfigGateway;
use Argo\Domain\Content\ContentLocator;
use Argo\Domain\DateTime;
use Argo\Domain\Json;
use Argo\Domain\Storage;
use Argo\Infra\System;

abstract class TestCase extends \PHPUnit\Framework\TestCase
{
    protected $container;

    protected $storage;

    protected $config;

    protected $configGateway;

    protected $content;

    protected $stdout;

    protected $stderr;

    protected $system;

    protected function setUp() : void
    {
        $tmp = __DIR__ . '/tmp';
        exec("rm -rf $tmp/*");
        exec("rm -rf $tmp/.config");

        $this->stdout = fopen('php://memory', 'w+');
        $this->stderr = fopen('php://memory', 'w+');

        $this->container = TestContainerFactory::new();
        $this->dateTime = $this->container->get(DateTime::CLASS);
        $this->storage = $this->container->get(Storage::CLASS);
        $this->content = $this->container->get(ContentLocator::CLASS);
        $this->config = $this->container->get(Config::CLASS);
        $this->configGateway = $this->container->get(ConfigGateway::CLASS);
        $this->system = $this->container->get(System::CLASS);

        $this->setDateTimeNow('0001-02-03 12:34:56');
    }

    protected function assertJsonEquals(
        /* array|object */ $expect,
        string $actual
    ) : void
    {
        $this->assertEquals(trim(Json::encode($expect)), trim($actual));
    }

    protected function varExport($var) : void
    {
        $str = var_export($var, true);
        $str = preg_replace('/=> *\n* */m', '=> ', $str);
        $str = preg_replace('/array *\(+/m', '[', $str);
        $str = preg_replace('/^( *)\)(.*)/m', '$1]$2', $str);
        $str = preg_replace('/^(  )( *) (\'.*)/m', '$1$2$3', $str);
        $str = preg_replace('/( +)(.*)/m', '$1$1$2', $str);
        $str = preg_replace_callback(
            '/(TRUE|FALSE|NULL),$/m',
            function ($matches) { return strtolower($matches[0]); },
            $str
        );
        echo "\n\n$str\n\n";
    }

    protected function setDateTimeNow(string $now) : string
    {
        $this->dateTime->now = $now;
        return $now;
    }

    protected function modDateTimeNow(string $modifier) : string
    {
        return $this->dateTime->modify($modifier);
    }
}


code:
<?php
declare(strict_types=1);

namespace Argo;

use Argo\App\AppContainerFactory;
use Argo\Domain\DateTime;
use Argo\Domain\FakeDateTime;
use Argo\Domain\Log;
use Argo\Infra\FakeLog;
use Argo\Infra\FakeServer;
use Argo\Infra\FakeSystem;
use Argo\Infra\Server;
use Argo\Infra\System;
use Capsule\Di\Container;
use Capsule\Di\Definitions;

class TestContainerFactory extends AppContainerFactory
{
    static protected function define() : Definitions
    {
        $def = parent::define();

        $def->object(DateTime::CLASS, FakeDateTime::CLASS);
        $def->object(System::CLASS, FakeSystem::CLASS);
        $def->object(Server::CLASS, FakeServer::CLASS);
        $def->object(Log::CLASS, FakeLog::CLASS);

        return $def;
    }
}


code:
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain

repo_name: aelina/rjw

path: .gitattributes


code:
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
*.csproj.user

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# DNX
project.lock.json
project.fragment.lock.json
artifacts/

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# JetBrains Rider
.idea/
*.sln.iml

# CodeRush
.cr/

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
/Archived
/MyTo-Do.txt
/LL_post.txt
/creditsnew.txt
/Assemblies/UnityEngine.UI.dll
/Assemblies/UnityEngine.dll
/Assemblies/TextMeshPro-1.0.55.56.0b11.dll
/Assemblies/NVorbis.dll
/Assemblies/NAudio.dll
/Assemblies/HugsLib.dll
/Assemblies/Assembly-CSharp.dll
/Assemblies/Assembly-CSharp-firstpass.dll
/Assemblies/0Harmony.dll
/RimJobWorld.Main.sln
/.gitignore
/1.3/Assemblies/UnityEngine.SubsystemsModule.dll
/1.3/Assemblies/HarmonyMod.dll
/1.3/Assemblies/0Harmony.dll
/1.3/Assemblies/HugsLib.xml
/1.3/Assemblies/HugsLib.dll

#dds files
*.dds

path: .gitignore


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
	<rjw.BackstoryDef>
		<defName>rjw_childC</defName>
		<baseDescription>[PAWN_nameDef] was born in civil settlement.</baseDescription>
		<title>Child</title>
		<titleShort>Child</titleShort>
		<slot>Childhood</slot>
		<categoryName>Civil</categoryName>
	</rjw.BackstoryDef>

	<rjw.BackstoryDef>
		<defName>rjw_childT</defName>
		<baseDescription>[PAWN_nameDef] was born in a tribe.</baseDescription>
		<title>Child</title>
		<titleShort>Child</titleShort>
		<slot>Childhood</slot>
		<categoryName>Tribal</categoryName>
	</rjw.BackstoryDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
	<BodyPartDef>
		<defName>Genitals</defName>
		genitals
		<hitPoints>12</hitPoints>
		<frostbiteVulnerability>0.01</frostbiteVulnerability>
		<skinCovered>true</skinCovered>
		<solid>false</solid>
		<canSuggestAmputation>false</canSuggestAmputation>
		<bleedRate>1.0</bleedRate>
		<tags>

RJW_Fertility

		</tags>
	</BodyPartDef>

	<BodyPartDef>
		<defName>Chest</defName>
		chest
		<hitPoints>24</hitPoints>
		<frostbiteVulnerability>0.01</frostbiteVulnerability>
		<skinCovered>true</skinCovered>
		<solid>false</solid>
		<canSuggestAmputation>false</canSuggestAmputation>
		<bleedRate>1.0</bleedRate>
	</BodyPartDef>

	<BodyPartDef>
		<defName>Anus</defName>
		anus
		<hitPoints>8</hitPoints>
		<frostbiteVulnerability>0.01</frostbiteVulnerability>
		<skinCovered>true</skinCovered>
		<solid>false</solid>
		<canSuggestAmputation>false</canSuggestAmputation>
		<bleedRate>1.0</bleedRate>
	</BodyPartDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
	<BodyPartDef>
		<defName>MechGenitals</defName>
		mechanoid implanter
		<hitPoints>20</hitPoints>
		<skinCovered>true</skinCovered>
		<solid>true</solid>
		<canSuggestAmputation>false</canSuggestAmputation>
		<bleedRate>0</bleedRate>
		<frostbiteVulnerability>0.0</frostbiteVulnerability>
		<tags>

RJW_Fertility

		</tags>
	</BodyPartDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
	<BodyPartTagDef>
		<defName>RJW_Fertility</defName>
		<label>fertility_source</label>
		<description>Necessary for reproduction</description>
		<vital>false</vital>
	</BodyPartTagDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
	<DamageDef>
		<defName>ViralDamage</defName>
		<workerClass>DamageWorker_AddInjury</workerClass>
		<label>viral damage</label>
		<externalViolence>false</externalViolence>
		<deathMessage>{0} has died of a disease.</deathMessage>
		<hediff>VirusPerma</hediff>
		<hediffSkin>VirusPerma</hediffSkin>
		<hediffSolid>VirusPerma</hediffSolid>
		<makesBlood>false</makesBlood>
		<harmAllLayersUntilOutside>false</harmAllLayersUntilOutside>
	</DamageDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
	<ThingDef ParentName="MakeableDrugBase">
		<defName>Aphrodisiac</defName>
		aphrodisiac drug
		<description>A smokable drug made from humpshroom.</description>
		<graphicData>
			<texPath>Things/Item/Drug/Penoxycyline</texPath>
			<graphicClass>Graphic_StackCount</graphicClass>
		</graphicData>
		<rotatable>false</rotatable>
		<statBases>
			<WorkToMake>450</WorkToMake>
			<MarketValue>50</MarketValue>
			<Mass>0.05</Mass>
			<DeteriorationRate>6</DeteriorationRate>
			<Flammability>1.3</Flammability>
		</statBases>
		<techLevel>Medieval</techLevel>
		<ingestible>
			<foodType>Plant, Processed</foodType>
			<joyKind>Chemical</joyKind>
			<joy>0.80</joy>
			<baseIngestTicks>720</baseIngestTicks>
			<nurseable>true</nurseable>
			<drugCategory>Social</drugCategory>
			<ingestSound>Ingest_Smoke</ingestSound>
			<ingestEffect>Smoke_Joint</ingestEffect>
			<ingestEffectEat>EatVegetarian</ingestEffectEat>
			<ingestHoldOffsetStanding>
				<northDefault>
					<offset>(0.27,0,0.08)</offset>
					<behind>true</behind>
				</northDefault>
				<east>
					<offset>(0.45,0,0.08)</offset>
				</east>
				<south>
					<offset>(0.27,0,0.08)</offset>
				</south>
				<west>
					<offset>(-0.50,0,0.08)</offset>
					<flip>true</flip>
				</west>
			</ingestHoldOffsetStanding>
			<ingestHoldUsesTable>false</ingestHoldUsesTable>
			<ingestCommandString>Smoke {0}</ingestCommandString>
			<ingestReportString>Smoking {0}.</ingestReportString>
			<ingestReportStringEat>Consuming {0}.</ingestReportStringEat>
			<useEatingSpeedStat>false</useEatingSpeedStat>
			<outcomeDoers>

					<hediffDef>HumpShroomEffect</hediffDef>
					<severity>0.6</severity>
					<toleranceChemical>HumpShroom</toleranceChemical>

					<hediffDef>HumpShroomTolerance</hediffDef>
					<severity>0.042</severity>
					<divideByBodySize>true</divideByBodySize>

					<need>Sex</need>
					<offset>-0.5</offset>

			</outcomeDoers>
		</ingestible>
		<recipeMaker>
			<recipeUsers>

CraftingSpot

DrugLab

			</recipeUsers>
			<workSpeedStat>DrugCookingSpeed</workSpeedStat>
			<workSkill>Cooking</workSkill>
		</recipeMaker>
		<costList>
			<HumpShroom>4</HumpShroom>
		</costList>
		<comps>

				<chemical>HumpShroom</chemical>
				<addictiveness>0.030</addictiveness>
				<minToleranceToAddict>0.15</minToleranceToAddict>
				<existingAddictionSeverityOffset>0.1</existingAddictionSeverityOffset>
				<needLevelOffset>1</needLevelOffset>
				<listOrder>30</listOrder>

		</comps>
	</ThingDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>

	<ThingDef ParentName="MakeableDrugPillBase">
		<defName>RJW_Contraceptive</defName>
		contraception pill
		<description>Deliver severe hit to fertility for one quadrum.</description>
		<graphicData>
			<texPath>Things/Item/Contraceptive</texPath>
			<graphicClass>Graphic_StackCount</graphicClass>
		</graphicData>
		<rotatable>false</rotatable>
		<statBases>
			<WorkToMake>120</WorkToMake>
			<MarketValue>15</MarketValue>
			<Mass>0.05</Mass>
		</statBases>
		<techLevel>Industrial</techLevel>
		<ingestible>
			<drugCategory>Medical</drugCategory>
			<outcomeDoers>

					<hediffDef>RJW_Contraceptive</hediffDef>
					<severity>1.0</severity>

			</outcomeDoers>
		</ingestible>
		<recipeMaker>
			<researchPrerequisite>DrugProduction</researchPrerequisite>
			<recipeUsers>

DrugLab

			</recipeUsers>
		</recipeMaker>
		<costList>
			<MedicineHerbal>1</MedicineHerbal>
		</costList>
		<comps>

			<addictiveness>0</addictiveness>
			<listOrder>1000</listOrder>
				<overdoseSeverityOffset>
					<min>0.08</min>
					<max>0.14</max>
				</overdoseSeverityOffset>

		</comps>
	</ThingDef>

	<HediffDef>
		<defName>RJW_Contraceptive</defName>
		<hediffClass>HediffWithComps</hediffClass>
		contraceptive
		<description>Contraception pills deliver severe hit to fertility for one quadrum.</description>
		<defaultLabelColor>(0.75, 0.75, 1.0)</defaultLabelColor>
		<isBad>false</isBad>
		<comps>

				<disappearsAfterTicks>
					<min>900000</min>
					<max>900000</max>
				</disappearsAfterTicks>

		</comps>
		<stages>

				Contracepted
				<capMods>

						<capacity>RJW_Fertility</capacity>
						<setMax>0.1</setMax>

				</capMods>

		</stages>
	</HediffDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>

	<ThingDef ParentName="MakeableDrugPillBase">
		<defName>RJW_FertPill</defName>
		hyperfertility pill
		<description>Enhances fertility for 48 hours. Also works as a mild aphrodisiac.</description>
		<graphicData>
			<texPath>Things/Item/FertPill</texPath>
			<graphicClass>Graphic_StackCount</graphicClass>
		</graphicData>
		<rotatable>false</rotatable>
		<statBases>
			<WorkToMake>120</WorkToMake>
			<MarketValue>15</MarketValue>
			<Mass>0.05</Mass>
		</statBases>
		<techLevel>Industrial</techLevel>
		<ingestible>
			<drugCategory>Medical</drugCategory>
			<outcomeDoers>

					<hediffDef>RJW_FertPill</hediffDef>
					<severity>1.0</severity>

					<need>Sex</need>
					<offset>-0.25</offset>

			</outcomeDoers>
		</ingestible>
		<recipeMaker>
			<researchPrerequisite>DrugProduction</researchPrerequisite>
			<recipeUsers>

DrugLab

			</recipeUsers>
		</recipeMaker>
		<costList>
			<MedicineHerbal>1</MedicineHerbal>
			<HumpShroom>1</HumpShroom>
		</costList>
		<comps>

			<addictiveness>0</addictiveness>
			<listOrder>1000</listOrder>
				<overdoseSeverityOffset>
					<min>0.08</min>
					<max>0.14</max>
				</overdoseSeverityOffset>

		</comps>
	</ThingDef>

	<HediffDef>
		<defName>RJW_FertPill</defName>
		<hediffClass>HediffWithComps</hediffClass>
		fertpill
		<description>Enhanced fertility. A mild aphrodisiac.</description>
		<defaultLabelColor>(0.75, 0.75, 1.0)</defaultLabelColor>
		<isBad>false</isBad>
		<comps>

				<disappearsAfterTicks>
					<min>120000</min>
					<max>120000</max>
				</disappearsAfterTicks>

		</comps>
		<stages>

				Hyperfertility
				<capMods>

						<capacity>RJW_Fertility</capacity>
						<offset>4</offset>

				</capMods>

		</stages>
	</HediffDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>

	<ThingDef ParentName="CavePlantBase">
		<defName>PlantHumpShroom</defName>
		humpshroom shroom
		<description>Thick shroom with tough flesh, thick juice and smooth soft skin. Exhibits aphrodisiac effects when consumed, refills psyfocus.</description>
		<statBases>
			<MaxHitPoints>85</MaxHitPoints>
			<Beauty>2</Beauty>
			<Nutrition>0.15</Nutrition>
			<MeditationFocusStrength>1.0</MeditationFocusStrength>
		</statBases>
		<graphicData>
			<texPath>Things/Item/Humpshroom/Humpshroom</texPath>
			<graphicClass>Graphic_Single</graphicClass>
		</graphicData>
		<selectable>true</selectable>
		<pathCost>10</pathCost>
		<plant>
			<fertilityMin>0.01</fertilityMin>
			<fertilitySensitivity>0.25</fertilitySensitivity>
			<growDays>15.00</growDays>
			<harvestTag>Standard</harvestTag>
			<harvestedThingDef>HumpShroom</harvestedThingDef>
			<harvestYield>2</harvestYield>
			<sowMinSkill>4</sowMinSkill>
			<sowTags>

Hydroponic

			</sowTags>
			<topWindExposure>0.1</topWindExposure>
			<visualSizeRange>0.4~0.7</visualSizeRange>
		</plant>
		<comps>

				<glowRadius>1</glowRadius>
				<glowColor>(255,105,180,0)</glowColor>

				<statDef>MeditationFocusStrength</statDef>
				<focusTypes>

Sex

				</focusTypes>

		</comps>
	</ThingDef>

	<ThingDef ParentName="DrugBase">
		<defName>HumpShroom</defName>
		Hump shroom
		<description>A shroom with tough flesh, thick juice and smooth soft skin. Exhibits aphrodisiac effects when consumed, refills psyfocus.</description>
		<tradeability>Sellable</tradeability>
		<socialPropernessMatters>true</socialPropernessMatters>
		<tickerType>Rare</tickerType>
		<graphicData>
			<texPath>Things/Item/Humpshroom/HumpshroomCollected</texPath>
			<graphicClass>Graphic_Single</graphicClass>
		</graphicData>
		<thingCategories>

PlantFoodRaw

		</thingCategories>
		<statBases>
			<MarketValue>10</MarketValue>
			<Mass>0.1</Mass>
			<DeteriorationRate>4</DeteriorationRate>
			<Nutrition>0.40</Nutrition>
		</statBases>
		<techLevel>Neolithic</techLevel>
		<ingestible>
			<baseIngestTicks>100</baseIngestTicks>
			<chairSearchRadius>4</chairSearchRadius>
			<preferability>RawTasty</preferability>
			<tasteThought></tasteThought>
			<foodType>VegetableOrFruit</foodType>
			<maxNumToIngestAtOnce>1</maxNumToIngestAtOnce>
			<optimalityOffsetHumanlikes>-6</optimalityOffsetHumanlikes>
			<optimalityOffsetFeedingAnimals>-11</optimalityOffsetFeedingAnimals>
			<ingestEffect>EatVegetarian</ingestEffect>
			<ingestSound>RawVegetable_Eat</ingestSound>
			<joy>0.5</joy>
			<joyKind>Social</joyKind>
			<nurseable>true</nurseable>
			<drugCategory>Social</drugCategory>
			<outcomeDoers>

					<hediffDef>HumpShroomEffect</hediffDef>
					<severity>0.50</severity>
					<toleranceChemical>HumpShroom</toleranceChemical>

					<hediffDef>HumpShroomTolerance</hediffDef>
					<severity>0.032</severity>
					<divideByBodySize>true</divideByBodySize>

					<need>Sex</need>
					<offset>-0.5</offset>

					<offset>1.0</offset>

		</outcomeDoers>
		</ingestible>
		<comps>

				<daysToRotStart>30</daysToRotStart>
				<rotDestroys>true</rotDestroys>

				<chemical>HumpShroom</chemical>
				<addictiveness>0.050</addictiveness>
				<minToleranceToAddict>0.15</minToleranceToAddict>
				<existingAddictionSeverityOffset>0.1</existingAddictionSeverityOffset>
				<needLevelOffset>1</needLevelOffset>
				<listOrder>30</listOrder>

		</comps>
	</ThingDef>

	<HediffDef>
		<defName>HumpShroomEffect</defName>
		<hediffClass>HediffWithComps</hediffClass>
		Induced libido
		<description>Hump shroom aphrodisiac effects.</description>
		<defaultLabelColor>(1,0,0.5)</defaultLabelColor>
		<scenarioCanAdd>true</scenarioCanAdd>
		<maxSeverity>1.0</maxSeverity>
		<comps>

				<severityPerDay>-0.9</severityPerDay>

		</comps>
		<stages>

				<restFallFactor>1.33</restFallFactor>
				<statOffsets>
					<SexFrequency>2</SexFrequency>
					<SexSatisfaction>0.5</SexSatisfaction>
				</statOffsets>

		</stages>
	</HediffDef>

	<ThoughtDef>
		<defName>HumpShroomEffect</defName>
		<workerClass>ThoughtWorker_Hediff</workerClass>
		<hediff>HumpShroomEffect</hediff>
		<validWhileDespawned>true</validWhileDespawned>
		<stages>

				Humpshroom aftertaste
				<description>Tastes funny.</description>
				<baseMoodEffect>1</baseMoodEffect>

		</stages>
	</ThoughtDef>

	<ChemicalDef>
		<defName>HumpShroom</defName>
		humpshroom
		<addictionHediff>HumpShroomAddiction</addictionHediff>
		<toleranceHediff>HumpShroomTolerance</toleranceHediff>
		<onGeneratedAddictedToleranceChance>0.8</onGeneratedAddictedToleranceChance>
	</ChemicalDef>

	<NeedDef ParentName="DrugAddictionNeedBase">
		<defName>Chemical_HumpShroom</defName>
		<needClass>Need_Chemical</needClass>
		humpshroom
		<description>Person is now hooked on shrooms. Without them, their bedroom performance may fall.</description>
		<listPriority>10</listPriority>
	</NeedDef>

	<HediffDef ParentName="DrugToleranceBase">
		<defName>HumpShroomTolerance</defName>
		humpshroom tolerance
		<description>Hump shroom effects tolerance.</description>
		<isBad>false</isBad>
		<comps>

				<severityPerDay>-0.020</severityPerDay>

				<chemical>HumpShroom</chemical>

		</comps>
	</HediffDef>

	<HediffDef ParentName="AddictionBase">
		<defName>HumpShroomAddiction</defName>
		<hediffClass>Hediff_Addiction</hediffClass>
		humpshroom addiction
		<description>Hump shroom effects addiction.</description>
		<causesNeed>Chemical_HumpShroom</causesNeed>
		<comps>

				<severityPerDay>-0.1</severityPerDay>
				<showDaysToRecover>true</showDaysToRecover>

		</comps>
		<stages>

				withdrawal
				<statOffsets>
					<SexSatisfaction>-1</SexSatisfaction>
				</statOffsets>

		</stages>
	</HediffDef>

	<ThoughtDef>
		<defName>HumpShroomWithdrawal</defName>
		<workerClass>ThoughtWorker_Hediff</workerClass>
		<hediff>HumpShroomAddiction</hediff>
		<validWhileDespawned>true</validWhileDespawned>
		<stages>

				<visible>false</visible>

				humpshroom withdrawal
				<description>I just feel nothing down there.</description>
				<baseMoodEffect>-5</baseMoodEffect>

		</stages>
	</ThoughtDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
	<HediffDef Name="BondageBase" Abstract="True">
		<hediffClass>Hediff</hediffClass>
		<defaultLabelColor>(0.5, 0.5, 0.9)</defaultLabelColor>
		<makesSickThought>false</makesSickThought>
		<isBad>false</isBad>
		<tendable>false</tendable>
		<scenarioCanAdd>false</scenarioCanAdd>
	</HediffDef>

	<HediffDef ParentName="BondageBase">
		<defName>Chains</defName>
		chains
		<labelNoun>chains</labelNoun>
		<description>A series of chains that restrict its wearer.</description>
		<stages>

				<capMods>

						<capacity>Manipulation</capacity>
						<setMax>0.35</setMax>

						<capacity>Moving</capacity>
						<setMax>0.35</setMax>

				 </capMods>

		</stages>
	</HediffDef>

	<HediffDef ParentName="BondageBase">
		<defName>BoundHands</defName>
		bound hands
		<labelNoun>bound hands</labelNoun>
		<description>Bound hands.</description>
		<stages>

				<capMods>

						<capacity>Manipulation</capacity>
						<setMax>0.00</setMax>

				</capMods>

		</stages>
	</HediffDef>

	<HediffDef ParentName="BondageBase">
		<defName>BoundLegs</defName>
		bound legs
		<labelNoun>bound legs</labelNoun>
		<description>Bound legs.</description>
		<stages>

				<capMods>

						<capacity>Moving</capacity>
						<setMax>0.00</setMax>

				</capMods>

		</stages>
	</HediffDef>

	<HediffDef ParentName="BondageBase">
		<defName>RJW_Restraints</defName>
		Restraints
		<labelNoun>restraints</labelNoun>
		<description>A set of restraints.</description>
		<stages>

				<capMods>

						<capacity>Manipulation</capacity>
						<setMax>0.00</setMax>

						<capacity>Moving</capacity>
						<setMax>0.00</setMax>

				</capMods>

		</stages>
	</HediffDef>

	<HediffDef ParentName="BondageBase">
		<defName>RJW_Cocoon</defName>
		<hediffClass>rjw.Cocoon</hediffClass>
		Cocoon
		<labelNoun>A cocoon</labelNoun>
		<description>A cocoon that tends to its host until ready to procreate.</description>
		<stages>

				<capMods>

						<capacity>Manipulation</capacity>
						<setMax>0.00</setMax>

						<capacity>Moving</capacity>
						<setMax>0.00</setMax>

				</capMods>
				<statOffsets>
					<ComfyTemperatureMin>-50</ComfyTemperatureMin>
					<ComfyTemperatureMax>50</ComfyTemperatureMax>
				</statOffsets>

		</stages>
	</HediffDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>

	<HediffDef>
		<defName>Hediff_Bukkake</defName>
		<hediffClass>rjw.Hediff_Bukkake</hediffClass>
		Bukkake
		<description>Ejaculated onto.</description>
		<makesSickThought>false</makesSickThought>
		<initialSeverity>0.01</initialSeverity>
		<maxSeverity>1</maxSeverity>

		<isBad>false</isBad>
		<tendable>false</tendable>
		<stages>

				minor
				<becomeVisible>false</becomeVisible>

				<minSeverity>0.3</minSeverity>
				little
				<statOffsets>
					<Vulnerability>0.2</Vulnerability>
					<SocialImpact>-0.1</SocialImpact>
				</statOffsets>

				<minSeverity>0.6</minSeverity>
				extensive
				<statOffsets>
					<Vulnerability>0.3</Vulnerability>
					<SocialImpact>-0.3</SocialImpact>
				</statOffsets>

				<minSeverity>0.8</minSeverity>
				full
				<statOffsets>
					<Vulnerability>-0.1</Vulnerability>
					<SocialImpact>-0.5</SocialImpact>
				</statOffsets>

		</stages>
	</HediffDef>

	<HediffDef Name="Hediff_Semen">
		<hediffClass>rjw.Hediff_Semen</hediffClass>
		<defName>Hediff_Semen</defName>
		semen
		<labelNoun>semen</labelNoun>
		<description>Semen.</description>
		<labelNounPretty>semen on {1}</labelNounPretty>
		<defaultLabelColor>(0.95,0.95,0.95)</defaultLabelColor>
		<isBad>false</isBad>
		<tendable>false</tendable>
		<makesSickThought>false</makesSickThought>
		<makesAlert>false</makesAlert>
		<maxSeverity>1</maxSeverity>
		<initialSeverity>0.001</initialSeverity>
		<injuryProps>
			<canMerge>true</canMerge>
		</injuryProps>
		<stages>

				little

				<minSeverity>0.25</minSeverity>
				some

				<minSeverity>0.5</minSeverity>
				dripping

				<minSeverity>0.8</minSeverity>
				drenched

		</stages>
		<comps>

				<healIntervalTicksStanding>1800</healIntervalTicksStanding>
				<healAmount>0.01</healAmount>

		</comps>
	</HediffDef>

	<HediffDef ParentName="Hediff_Semen">
		<defName>Hediff_InsectSpunk</defName>
		<description>Insect spunk.</description>
		insect spunk
		<labelNoun>insect spunk</labelNoun>
		<labelNounPretty>insect spunk on {1}</labelNounPretty>
		<defaultLabelColor>(0.6,0.83,0.35)</defaultLabelColor>
	</HediffDef>

	<HediffDef ParentName="Hediff_Semen">
		<defName>Hediff_MechaFluids</defName>
		<description>Mechanoid fluids.</description>
		mechanoid fluids
		<labelNoun>mechanoid fluids</labelNoun>
		<labelNounPretty>mecha fluids on {1}</labelNounPretty>
		<defaultLabelColor>(0.37,0.71,0.82)</defaultLabelColor>
	</HediffDef>
</Defs>