├── .gitignore ├── .idea └── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── CleanCodeCaseStudy.iml ├── FitNesseRoot ├── CleanCoders │ ├── EpisodeOnePresentCodeCasts │ │ ├── GivenNoCodecastsPresentNoCodecasts │ │ │ ├── content.txt │ │ │ └── properties.xml │ │ ├── content.txt │ │ └── properties.xml │ ├── EpisodeTwoAndThreePresentCodecasts │ │ ├── PresentDownloadableCodecastsInChronologicalOrder │ │ │ ├── content.txt │ │ │ └── properties.xml │ │ ├── PresentViewableCodecastsInChronologicalOrder │ │ │ ├── content.txt │ │ │ └── properties.xml │ │ ├── content.txt │ │ └── properties.xml │ ├── ScenarioLibrary │ │ ├── content.txt │ │ └── properties.xml │ ├── SetUp │ │ ├── content.txt │ │ └── properties.xml │ ├── ShowEpisode │ │ ├── content.txt │ │ └── properties.xml │ ├── content.txt │ └── properties.xml ├── FrontPage │ ├── content.txt │ └── properties.xml ├── PageFooter │ ├── content.txt │ └── properties.xml ├── PageHeader │ ├── content.txt │ └── properties.xml ├── TemplateLibrary │ ├── StaticPage │ │ ├── content.txt │ │ └── properties.xml │ ├── SuitePage │ │ ├── content.txt │ │ └── properties.xml │ ├── TestPage │ │ ├── content.txt │ │ └── properties.xml │ ├── content.txt │ └── properties.xml ├── properties ├── updateDoNotCopyOverList └── updateList ├── README.md ├── pom.xml ├── project.clj ├── resources └── html │ ├── codecast.html │ ├── contentAction.html │ ├── contentActions.html │ ├── details.html │ ├── detailsAction.html │ ├── detailsLicense.html │ ├── detailsPurchaseOption.html │ ├── frontpage.html │ └── purchaseOption.html ├── src └── cleancoderscom │ ├── Context.java │ ├── GateKeeper.java │ ├── entities │ ├── Codecast.java │ ├── Entity.java │ ├── License.java │ └── User.java │ ├── gateways │ ├── CodecastGateway.java │ ├── LicenseGateway.java │ └── UserGateway.java │ ├── http │ ├── Controller.java │ ├── ParsedRequest.java │ ├── RequestParser.java │ └── Router.java │ ├── socketserver │ ├── SocketServer.java │ └── SocketService.java │ ├── usecases │ ├── codecastDetails │ │ ├── CodecastDetailsUseCase.java │ │ └── PresentableCodecastDetails.java │ └── codecastSummaries │ │ ├── CodecastSummariesController.java │ │ ├── CodecastSummariesInputBoundary.java │ │ ├── CodecastSummariesOutputBoundary.java │ │ ├── CodecastSummariesPresenter.java │ │ ├── CodecastSummariesResponseModel.java │ │ ├── CodecastSummariesUseCase.java │ │ ├── CodecastSummariesView.java │ │ ├── CodecastSummariesViewImpl.java │ │ ├── CodecastSummariesViewModel.java │ │ └── CodecastSummary.java │ └── view │ └── ViewTemplate.java └── test └── cleancoderscom ├── TestSetup.java ├── doubles ├── GatewayUtilities.java ├── InMemoryCodecastGateway.java ├── InMemoryLicenseGateway.java └── InMemoryUserGateway.java ├── entities └── EntityTest.java ├── fixtures ├── CodecastDetails.java ├── CodecastPresentation.java ├── FixtureSetup.java ├── GivenCodecasts.java └── OfCodeCasts.java ├── http ├── RequestParserTest.java └── RouterTest.java ├── socketserver └── SocketServerTest.java ├── usecases ├── codecastDetails │ └── CodecastDetailsUseCaseTest.java └── codecastSummaries │ ├── CodecastSummariesControllerTest.java │ ├── CodecastSummariesInputBoundarySpy.java │ ├── CodecastSummariesOutputBoundarySpy.java │ ├── CodecastSummariesPresenterTest.java │ ├── CodecastSummariesUseCaseTest.java │ └── CodecastSummariesViewSpy.java ├── utilities ├── HelloWorld.java └── Main.java └── view └── ViewTemplateTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.zip 3 | FitNesseRoot/files 4 | FitNesseRoot/ErrorLogs 5 | FitNesseRoot/FitNesse 6 | FitNesseRoot/RecentChanges 7 | out 8 | cleancoderscom.iml 9 | fitnesse-standalone.jar 10 | target 11 | *camtasia 12 | .DS_Store 13 | .lein-failures 14 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /CleanCodeCaseStudy.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/EpisodeOnePresentCodeCasts/GivenNoCodecastsPresentNoCodecasts/content.txt: -------------------------------------------------------------------------------- 1 | -!|script| 2 | |given no codecasts | 3 | |given user U | 4 | |with user U logged in | 5 | |then the following codecasts will be presented for U | 6 | |there will be no codecasts presented | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/EpisodeOnePresentCodeCasts/GivenNoCodecastsPresentNoCodecasts/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | true 6 | true 7 | true 8 | true 9 | 10 | true 11 | true 12 | 13 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/EpisodeOnePresentCodeCasts/content.txt: -------------------------------------------------------------------------------- 1 | !1 Test suite for Episode 1 2 | !2 Present Codecasts 3 | !contents -R2 -g -p -f -h -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/EpisodeOnePresentCodeCasts/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | The suite of tests for episode 1. 6 | true 7 | true 8 | true 9 | true 10 | true 11 | true 12 | true 13 | 14 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/EpisodeTwoAndThreePresentCodecasts/PresentDownloadableCodecastsInChronologicalOrder/content.txt: -------------------------------------------------------------------------------- 1 | | Given Codecasts | 2 | | title | published | 3 | | A | 3/1/2014 | 4 | | B | 3/2/2014 | 5 | | C | 2/18/2014 | 6 | 7 | -!|script| 8 | |given user U| 9 | |with user U logged in| 10 | |and with license for U able to download A| 11 | |then the following codecasts will be presented for U| 12 | 13 | | Ordered query:of code casts | 14 | | title | picture | description | viewable | downloadable | 15 | | C | C | C | - | -| 16 | | A | A | A | - | + | 17 | | B | B | B | - | -| 18 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/EpisodeTwoAndThreePresentCodecasts/PresentDownloadableCodecastsInChronologicalOrder/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | true 6 | true 7 | true 8 | true 9 | true 10 | true 11 | true 12 | 13 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/EpisodeTwoAndThreePresentCodecasts/PresentViewableCodecastsInChronologicalOrder/content.txt: -------------------------------------------------------------------------------- 1 | | Given Codecasts | 2 | | title | published | 3 | | A | 3/1/2014 | 4 | | B | 3/2/2014 | 5 | | C | 2/18/2014 | 6 | 7 | -!|script| 8 | |given user U| 9 | |with user U logged in| 10 | |and with license for U able to view A| 11 | |then the following codecasts will be presented for U| 12 | 13 | | Ordered query:of code casts | 14 | | title | publication date | picture | description | viewable | downloadable | 15 | | C | | C | C | - | -| 16 | | A | | A | A | + | - | 17 | | B | | B | B | - | -| 18 | 19 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/EpisodeTwoAndThreePresentCodecasts/PresentViewableCodecastsInChronologicalOrder/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/EpisodeTwoAndThreePresentCodecasts/content.txt: -------------------------------------------------------------------------------- 1 | !1 Test suite for Episode 2 & 3 2 | !2 Present Codecasts 3 | 4 | !contents -R2 -g -p -f -h -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/EpisodeTwoAndThreePresentCodecasts/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | true 6 | true 7 | true 8 | true 9 | true 10 | true 11 | true 12 | 13 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/ScenarioLibrary/content.txt: -------------------------------------------------------------------------------- 1 | | scenario | given no codecasts | 2 | | clear codecasts | 3 | 4 | | scenario | with user _ logged in | user | 5 | | login user | @user | 6 | 7 | | scenario | and with license for _ able to view _ | user,codecast | 8 | | create license for | @user | viewing | @codecast | 9 | 10 | | scenario | and with license for _ able to download _ | user,codecast | 11 | | create license for | @user | downloading | @codecast | 12 | 13 | | scenario | then the following codecasts will be presented for _ | user | 14 | | check | presentation user | @user | 15 | 16 | | scenario | there will be no codecasts presented | 17 | | check | count of codecasts presented | 0 | 18 | 19 | | scenario | given user _ | user | 20 | | add user | @user | 21 | 22 | | scenario | when the user requests details for codecast _ | permalink | 23 | | request codecast | @permalink | 24 | 25 | | scenario | then the presented title is _, published _ | title,date | 26 | | check | codecast details title | @title | 27 | | check | codecast details date | @date | 28 | 29 | | scenario | with option to purchase _ license | licenseType | 30 | | codecast details offer purchase of | @licenseType | 31 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/ScenarioLibrary/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | true 6 | true 7 | true 8 | true 9 | true 10 | true 11 | 12 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/SetUp/content.txt: -------------------------------------------------------------------------------- 1 | | import | 2 | | cleancoderscom.fixtures | 3 | 4 | | library | 5 | | codecast presentation | 6 | | codecast details | 7 | 8 | | Fixture setup | 9 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/SetUp/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | true 6 | true 7 | true 8 | true 9 | true 10 | true 11 | 12 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/ShowEpisode/content.txt: -------------------------------------------------------------------------------- 1 | | Given Codecasts | 2 | | title | published | permalink | 3 | | A | 3/1/2014 | episode-1 | 4 | 5 | -!| script | 6 | | given user U | 7 | | with user U logged in | 8 | | when the user requests details for codecast episode-1 | 9 | | then the presented title is A, published 3/01/2014 | 10 | | with option to purchase viewing license | 11 | | with option to purchase download license | 12 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/ShowEpisode/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | true 6 | true 7 | true 8 | true 9 | true 10 | true 11 | true 12 | 13 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/content.txt: -------------------------------------------------------------------------------- 1 | !contents 2 | ---- 3 | !define TEST_SYSTEM {slim} 4 | !path target/classes 5 | !path target/test-classes 6 | -------------------------------------------------------------------------------- /FitNesseRoot/CleanCoders/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /FitNesseRoot/FrontPage/content.txt: -------------------------------------------------------------------------------- 1 | !1 Welcome to [[FitNesse][FitNesse.FitNesse]]! 2 | !3 ''The fully integrated stand-alone acceptance testing framework and wiki.'' 3 | # Here is a good place to add your first page (WikiWord). For example, MyTopLevelApplicationPage 4 | 5 | To add your first "page", click the [[Edit][FrontPage?edit]] button and add a [[!-WikiWord-!][FitNesse.UserGuide.FitNesseWiki.WikiWord]] to the page. 6 | 7 | | '''To Learn More...''' | 8 | | [[A One-Minute Description][.FitNesse.UserGuide.OneMinuteDescription]] | ''What is FitNesse Start here.'' | 9 | | [[A Two-Minute Example][.FitNesse.UserGuide.TwoMinuteExample]] | ''A brief example. Read this one next.'' | 10 | | [[User Guide][.FitNesse.UserGuide]] | ''Answer the rest of your questions here.'' | 11 | | [[Acceptance Tests][.FitNesse.SuiteAcceptanceTests]] | ''FitNesse's suite of Acceptance Tests'' | 12 | | [[Release Notes][.FitNesse.ReleaseNotes]] | ''Find out about FitNesse's new features'' | 13 | 14 | !note Release ${FITNESSE_VERSION} 15 | 16 | [[Clean Coders Case Study][.CleanCoders]] 17 | 18 | -------------------------------------------------------------------------------- /FitNesseRoot/FrontPage/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /FitNesseRoot/PageFooter/content.txt: -------------------------------------------------------------------------------- 1 | [[Front Page][.FrontPage]] | [[User Guide][.FitNesse.UserGuide]] | [[root][root]] (for global !-!path's-!, ''etc.'') | Press '?' for keyboard shortcuts [[(edit)][.PageFooter?edit]] -------------------------------------------------------------------------------- /FitNesseRoot/PageFooter/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 1238490732869 12 | -5498758380075500221 13 | 14 | -------------------------------------------------------------------------------- /FitNesseRoot/PageHeader/content.txt: -------------------------------------------------------------------------------- 1 | #page header -------------------------------------------------------------------------------- /FitNesseRoot/PageHeader/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /FitNesseRoot/TemplateLibrary/StaticPage/content.txt: -------------------------------------------------------------------------------- 1 | !contents -R2 -g -p -f -h -------------------------------------------------------------------------------- /FitNesseRoot/TemplateLibrary/StaticPage/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | true 6 | true 7 | true 8 | true 9 | true 10 | true 11 | 12 | -------------------------------------------------------------------------------- /FitNesseRoot/TemplateLibrary/SuitePage/content.txt: -------------------------------------------------------------------------------- 1 | !1 Test suite X 2 | !contents -R2 -g -p -f -h -------------------------------------------------------------------------------- /FitNesseRoot/TemplateLibrary/SuitePage/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | true 6 | true 7 | true 8 | true 9 | true 10 | true 11 | true 12 | 13 | -------------------------------------------------------------------------------- /FitNesseRoot/TemplateLibrary/TestPage/content.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /FitNesseRoot/TemplateLibrary/TestPage/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | true 6 | true 7 | true 8 | true 9 | true 10 | true 11 | true 12 | 13 | -------------------------------------------------------------------------------- /FitNesseRoot/TemplateLibrary/content.txt: -------------------------------------------------------------------------------- 1 | Here all the pages go that are available throughout the wiki. 2 | 3 | !contents -R2 -g -p -f -h -------------------------------------------------------------------------------- /FitNesseRoot/TemplateLibrary/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | Base template library 6 | true 7 | true 8 | true 9 | true 10 | true 11 | true 12 | 13 | -------------------------------------------------------------------------------- /FitNesseRoot/properties: -------------------------------------------------------------------------------- 1 | ##FitNesse properties 2 | #Wed Oct 07 09:14:18 CDT 2015 3 | Version=v20140630 4 | -------------------------------------------------------------------------------- /FitNesseRoot/updateDoNotCopyOverList: -------------------------------------------------------------------------------- 1 | FitNesseRoot/FrontPage/content.txt 2 | FitNesseRoot/FrontPage/properties.xml 3 | FitNesseRoot/PageFooter/content.txt 4 | FitNesseRoot/PageFooter/properties.xml 5 | FitNesseRoot/PageHeader/content.txt 6 | FitNesseRoot/PageHeader/properties.xml 7 | FitNesseRoot/TemplateLibrary/StaticPage/content.txt 8 | FitNesseRoot/TemplateLibrary/StaticPage/properties.xml 9 | FitNesseRoot/TemplateLibrary/SuitePage/content.txt 10 | FitNesseRoot/TemplateLibrary/SuitePage/properties.xml 11 | FitNesseRoot/TemplateLibrary/TestPage/content.txt 12 | FitNesseRoot/TemplateLibrary/TestPage/properties.xml 13 | FitNesseRoot/TemplateLibrary/content.txt 14 | FitNesseRoot/TemplateLibrary/properties.xml 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## The Code for the [cleancoders.com Case Study Series](http://cleancoders.com/category/java-case-study). 2 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | casestudy 4 | casestudy 5 | jar 6 | cleancoderscasestudy 7 | casestudy 8 | Case Study 9 | 10 | scm:git:git://github.com/cleancoders/CleanCodeCaseStudy.git 11 | scm:git:ssh://git@github.com/cleancoders/CleanCodeCaseStudy.git 12 | cb7c013982f4e12451e23b1774544582faea82ce 13 | 14 | https://github.com/cleancoders/CleanCodeCaseStudy 15 | 16 | 17 | src 18 | test 19 | 20 | 21 | resources 22 | 23 | 24 | 25 | 26 | dev-resources 27 | 28 | 29 | resources 30 | 31 | 32 | target 33 | target/classes 34 | 35 | 36 | 37 | 38 | central 39 | http://repo1.maven.org/maven2/ 40 | 41 | false 42 | 43 | 44 | true 45 | 46 | 47 | 48 | clojars 49 | https://clojars.org/repo/ 50 | 51 | true 52 | 53 | 54 | true 55 | 56 | 57 | 58 | sonatype 59 | http://oss.sonatype.org/content/repositories/releases 60 | 61 | true 62 | 63 | 64 | true 65 | 66 | 67 | 68 | 69 | 70 | org.clojure 71 | clojure 72 | 1.6.0 73 | 74 | 75 | junit 76 | junit 77 | 4.11 78 | 79 | 80 | org.fitnesse 81 | fitnesse 82 | 20140630 83 | 84 | 85 | de.bechte.junit 86 | junit-hierarchicalcontextrunner 87 | 4.11.3 88 | 89 | 90 | org.clojure 91 | tools.nrepl 92 | 0.2.3 93 | 94 | 95 | org.clojure 96 | clojure 97 | 98 | 99 | test 100 | 101 | 102 | clojure-complete 103 | clojure-complete 104 | 0.2.3 105 | 106 | 107 | org.clojure 108 | clojure 109 | 110 | 111 | test 112 | 113 | 114 | 115 | 116 | 120 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject casestudy "cleancoderscasestudy" 2 | :description "Case Study" 3 | :dependencies [[org.clojure/clojure "1.6.0"] 4 | [junit/junit "4.11"] 5 | [org.fitnesse/fitnesse "20140630"] 6 | [de.bechte.junit/junit-hierarchicalcontextrunner "4.11.3"]] 7 | 8 | :repositories [["sonatype" {:url "http://oss.sonatype.org/content/repositories/releases"}]] 9 | 10 | :aliases {"fitnesse" ["run" "-m" "fitnesseMain.FitNesseMain" "-p" "8080"]} 11 | ) 12 | 13 | 14 | -------------------------------------------------------------------------------- /resources/html/codecast.html: -------------------------------------------------------------------------------- 1 |
4 | 5 |
6 | 8 |
9 |
10 |
11 |
12 |
by ${author} | ${publicationDate} | ${duration}
15 |
16 |
17 |
18 |
19 |

Add to Cart

20 | 21 |
22 | 23 | ${licenseOptions} 24 |
25 |
26 |
27 | ${contentActions} 28 |
29 |
30 |
-------------------------------------------------------------------------------- /resources/html/contentAction.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/html/contentActions.html: -------------------------------------------------------------------------------- 1 |

Content

2 | 3 |
4 | ${actions} 5 |
6 |
-------------------------------------------------------------------------------- /resources/html/details.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Clean Coders 7 | 10 | 11 | 13 | 14 | 15 |
16 |
17 |
18 |
19 | 20 | 21 | 24 |
25 | 42 |
43 |
44 |
45 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |

Details

57 | 58 |
59 |
By ${author},${publicationDate},59 min

sha1: b68fb44c5b895a02e935fa54e794fbbff7a3c336
md5: f714a96edec49048e83d6ce0d00654b0 63 |
64 |
65 | ${actionsHTML} 66 |
67 |
68 |
69 |

Add to Cart

70 | 71 | 72 | 80 | 81 | 82 | ${purchaseOptionsHTML} 83 |
73 |
to 77 | 78 |
79 |
${licensePrice} more
84 |
    85 |
  • ${licenseType} License for ${ownerName}
  • 86 | ${licenseHTML} 87 |
88 |
89 |
90 |

Description

91 |
92 | ${description} 93 |
94 |
95 |
96 |
97 |
    98 |
  • 100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | 150 | 152 | 155 | 156 | -------------------------------------------------------------------------------- /resources/html/detailsAction.html: -------------------------------------------------------------------------------- 1 | ${action} -------------------------------------------------------------------------------- /resources/html/detailsLicense.html: -------------------------------------------------------------------------------- 1 |
  • ${licenseType} License for ${ownerName}
  • -------------------------------------------------------------------------------- /resources/html/detailsPurchaseOption.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
    to 7 | 8 |
    9 | 10 | ${licensePrice} more 11 | -------------------------------------------------------------------------------- /resources/html/frontpage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Clean Coders 6 | 7 | 9 | 11 | 13 | 14 | 15 |
    16 |
    17 |
    18 | 19 | 20 | 33 |
    34 |
    35 |
    36 |
    37 |
    38 |
    39 |
    40 |
    Training Videos.With Personality.For Software Professionals.
    42 | See All Videos
    43 |
    44 |
    45 | 67 |
    68 |
    69 |
    70 |
    71 |

    All Episodes

    72 | 73 | 77 | 82 |
    83 |
    84 | ${codecasts} 85 |
    86 | 158 |
    159 |
    160 |
    161 | 165 |
    166 |
    167 |
    168 | 176 |
    © 2014 Clean Coders, LLC.
    All Rights Reserved.
    177 |
    178 |
    Get Clean Coders Emails 180 |
    181 |
    182 | 183 |
    184 |
    185 | 190 |
    194 |
    195 |
    196 |
    197 |
    198 | 199 | 201 | 203 | 205 | 207 | 208 | -------------------------------------------------------------------------------- /resources/html/purchaseOption.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
    4 | to 5 | 6 |
    7 | 8 | ${licensePrice} 9 | -------------------------------------------------------------------------------- /src/cleancoderscom/Context.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom; 2 | 3 | import cleancoderscom.gateways.CodecastGateway; 4 | import cleancoderscom.gateways.LicenseGateway; 5 | import cleancoderscom.gateways.UserGateway; 6 | 7 | public class Context { 8 | public static UserGateway userGateway; 9 | public static CodecastGateway codecastGateway; 10 | public static LicenseGateway licenseGateway; 11 | public static GateKeeper gateKeeper; 12 | } 13 | -------------------------------------------------------------------------------- /src/cleancoderscom/GateKeeper.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom; 2 | 3 | import cleancoderscom.entities.User; 4 | 5 | public class GateKeeper { 6 | private User loggedInUser; 7 | 8 | public void setLoggedInUser(User loggedInUser) { 9 | this.loggedInUser = loggedInUser; 10 | } 11 | 12 | public User getLoggedInUser() { 13 | return loggedInUser; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/cleancoderscom/entities/Codecast.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.entities; 2 | 3 | import cleancoderscom.entities.Entity; 4 | 5 | import java.util.Date; 6 | 7 | public class Codecast extends Entity { 8 | private String title; 9 | private Date publicationDate = new Date(); 10 | private String permalink; 11 | 12 | public void setTitle(String title) { 13 | this.title = title; 14 | } 15 | 16 | public void setPublicationDate(Date publicationDate) { 17 | this.publicationDate = publicationDate; 18 | } 19 | 20 | public String getTitle() { 21 | return title; 22 | } 23 | 24 | public Date getPublicationDate() { 25 | return publicationDate; 26 | } 27 | 28 | public void setPermalink(String permalink) { 29 | this.permalink = permalink; 30 | } 31 | 32 | public String getPermalink() { 33 | return permalink; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/cleancoderscom/entities/Entity.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.entities; 2 | 3 | import java.util.Objects; 4 | 5 | public class Entity implements Cloneable { 6 | private String id; 7 | 8 | public boolean isSame(Entity entity) { 9 | return id != null && Objects.equals(id, entity.id); 10 | } 11 | 12 | public void setId(String id) { 13 | this.id = id; 14 | } 15 | 16 | public String getId() { 17 | return id; 18 | } 19 | 20 | public Object clone() throws CloneNotSupportedException { 21 | return super.clone(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/cleancoderscom/entities/License.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.entities; 2 | 3 | public class License extends Entity { 4 | public enum LicenseType {DOWNLOADING, VIEWING;} 5 | private LicenseType type; 6 | 7 | private User user; 8 | private Codecast codecast; 9 | public License(LicenseType type, User user, Codecast codecast) { 10 | this.type = type; 11 | this.user = user; 12 | this.codecast = codecast; 13 | } 14 | 15 | public LicenseType getType() { 16 | return type; 17 | } 18 | 19 | public User getUser() { 20 | return user; 21 | } 22 | 23 | public Codecast getCodecast() { 24 | return codecast; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/cleancoderscom/entities/User.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.entities; 2 | 3 | import cleancoderscom.entities.Entity; 4 | 5 | public class User extends Entity { 6 | private String userName; 7 | 8 | public User(String userName) { 9 | this.userName = userName; 10 | } 11 | 12 | public String getUserName() { 13 | return userName; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/cleancoderscom/gateways/CodecastGateway.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.gateways; 2 | 3 | import cleancoderscom.entities.Codecast; 4 | 5 | import java.util.List; 6 | 7 | public interface CodecastGateway { 8 | List findAllCodecastsSortedChronologically(); 9 | 10 | void delete(Codecast codecast); 11 | 12 | Codecast save(Codecast codecast); 13 | 14 | Codecast findCodecastByTitle(String codecastTitle); 15 | 16 | Codecast findCodecastByPermalink(String permalink); 17 | } 18 | -------------------------------------------------------------------------------- /src/cleancoderscom/gateways/LicenseGateway.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.gateways; 2 | 3 | import cleancoderscom.entities.Codecast; 4 | import cleancoderscom.entities.License; 5 | import cleancoderscom.entities.User; 6 | 7 | import java.util.List; 8 | 9 | public interface LicenseGateway { 10 | License save(License license); 11 | 12 | List findLicensesForUserAndCodecast(User user, Codecast codecast); 13 | } 14 | -------------------------------------------------------------------------------- /src/cleancoderscom/gateways/UserGateway.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.gateways; 2 | 3 | import cleancoderscom.entities.User; 4 | 5 | public interface UserGateway { 6 | User save(User user); 7 | 8 | User findUserByName(String username); 9 | } 10 | -------------------------------------------------------------------------------- /src/cleancoderscom/http/Controller.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.http; 2 | 3 | 4 | public interface Controller { 5 | String handle(ParsedRequest request); 6 | } -------------------------------------------------------------------------------- /src/cleancoderscom/http/ParsedRequest.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.http; 2 | 3 | public class ParsedRequest { 4 | public String method = ""; 5 | public String path = ""; 6 | 7 | public ParsedRequest(String method, String path) { 8 | this.method = method; 9 | this.path = path; 10 | } 11 | 12 | public ParsedRequest() { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/cleancoderscom/http/RequestParser.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.http; 2 | 3 | public class RequestParser { 4 | 5 | 6 | public ParsedRequest parse(String requestString) { 7 | ParsedRequest request = new ParsedRequest(); 8 | if(requestString != null) { 9 | String[] parts = requestString.split(" "); 10 | if (parts.length >= 1) 11 | request.method = parts[0]; 12 | if (parts.length >= 2) 13 | request.path = parts[1]; 14 | } 15 | return request; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/cleancoderscom/http/Router.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.http; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class Router { 7 | private Map routes = new HashMap<>(); 8 | 9 | public void addPath(String path, Controller controller) { 10 | routes.put(path, controller); 11 | } 12 | 13 | public String route(ParsedRequest request) { 14 | String[] parts = request.path.split("/"); 15 | String controllerKey = parts.length > 1 ? parts[1] : ""; 16 | Controller controller = routes.get(controllerKey); 17 | if(controller == null) { 18 | return "HTTP/1.1 404 OK\n"; 19 | } else { 20 | return controller.handle(request); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/cleancoderscom/socketserver/SocketServer.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.socketserver; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | public class SocketServer { 11 | private final int port; 12 | private final SocketService service; 13 | private boolean running; 14 | private ServerSocket serverSocket; 15 | private ExecutorService executor; 16 | 17 | public SocketServer(int port, SocketService service) throws Exception { 18 | this.port = port; 19 | this.service = service; 20 | serverSocket = new ServerSocket(port); 21 | executor = Executors.newFixedThreadPool(4); 22 | } 23 | 24 | public void start() throws Exception { 25 | Runnable connectionHandler = new Runnable() { 26 | public void run() { 27 | try { 28 | while(running) { 29 | Socket serviceSocket = serverSocket.accept(); 30 | executor.execute(new Runnable() { 31 | @Override 32 | public void run() { 33 | service.serve(serviceSocket); 34 | } 35 | }); 36 | } 37 | } catch(IOException e) { 38 | if(running) 39 | e.printStackTrace(); 40 | } 41 | } 42 | }; 43 | executor.execute(connectionHandler); 44 | 45 | running = true; 46 | } 47 | 48 | public void stop() throws Exception { 49 | running = false; 50 | serverSocket.close(); 51 | executor.shutdown(); 52 | executor.awaitTermination(1000, TimeUnit.MILLISECONDS); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/cleancoderscom/socketserver/SocketService.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.socketserver; 2 | 3 | import java.net.Socket; 4 | 5 | public interface SocketService { 6 | void serve(Socket s); 7 | } 8 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastDetails/CodecastDetailsUseCase.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastDetails; 2 | 3 | import cleancoderscom.entities.Codecast; 4 | import cleancoderscom.Context; 5 | import cleancoderscom.entities.User; 6 | 7 | public class CodecastDetailsUseCase { 8 | public PresentableCodecastDetails requestCodecastDetails(User loggedInUser, String permalink) { 9 | PresentableCodecastDetails details = new PresentableCodecastDetails(); 10 | 11 | Codecast codecast = Context.codecastGateway.findCodecastByPermalink(permalink); 12 | if (codecast == null) { 13 | details.wasFound = false; 14 | return details; 15 | } 16 | else { 17 | details.wasFound = true; 18 | return details; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastDetails/PresentableCodecastDetails.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastDetails; 2 | 3 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesResponseModel; 4 | 5 | public class PresentableCodecastDetails extends CodecastSummariesResponseModel 6 | { 7 | public boolean wasFound; 8 | } 9 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastSummaries/CodecastSummariesController.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import cleancoderscom.Context; 4 | import cleancoderscom.entities.User; 5 | import cleancoderscom.http.Controller; 6 | import cleancoderscom.http.ParsedRequest; 7 | 8 | public class CodecastSummariesController implements Controller { 9 | private CodecastSummariesInputBoundary useCase; 10 | private final CodecastSummariesOutputBoundary presenter; 11 | private final CodecastSummariesView view; 12 | 13 | public CodecastSummariesController(CodecastSummariesInputBoundary useCase, 14 | CodecastSummariesOutputBoundary presenter, 15 | CodecastSummariesView view) { 16 | this.useCase = useCase; 17 | this.presenter = presenter; 18 | this.view = view; 19 | } 20 | 21 | public String handle(ParsedRequest request) { 22 | final User user = Context.gateKeeper.getLoggedInUser(); 23 | useCase.summarizeCodecasts(user, presenter); 24 | return view.generateView(presenter.getViewModel()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastSummaries/CodecastSummariesInputBoundary.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import cleancoderscom.entities.User; 4 | 5 | public interface CodecastSummariesInputBoundary 6 | { 7 | void summarizeCodecasts(User loggedInUser, CodecastSummariesOutputBoundary presenter); 8 | } 9 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastSummaries/CodecastSummariesOutputBoundary.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | public interface CodecastSummariesOutputBoundary 4 | { 5 | CodecastSummariesViewModel getViewModel(); 6 | 7 | void present(CodecastSummariesResponseModel responseModel); 8 | } 9 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastSummaries/CodecastSummariesPresenter.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import java.text.SimpleDateFormat; 4 | import static cleancoderscom.usecases.codecastSummaries.CodecastSummariesViewModel.*; 5 | 6 | public class CodecastSummariesPresenter implements CodecastSummariesOutputBoundary { 7 | 8 | public static SimpleDateFormat dateFormat = new SimpleDateFormat("M/dd/yyyy"); 9 | public CodecastSummariesViewModel viewModel; 10 | 11 | @Override 12 | public CodecastSummariesViewModel getViewModel() { 13 | return viewModel; 14 | } 15 | 16 | @Override 17 | public void present(CodecastSummariesResponseModel responseModel) { 18 | viewModel = new CodecastSummariesViewModel(); 19 | for(CodecastSummary codecastSummary : responseModel.getCodecastSummaries()) { 20 | viewModel.addModel(makeViewable(codecastSummary)); 21 | } 22 | } 23 | 24 | private ViewableCodecastSummary makeViewable(CodecastSummary codecastSummary) { 25 | final ViewableCodecastSummary summary = new ViewableCodecastSummary(); 26 | summary.title = codecastSummary.title; 27 | summary.permalink = codecastSummary.permalink; 28 | summary.publicationDate = dateFormat.format(codecastSummary.publicationDate); 29 | summary.isDownloadable = codecastSummary.isDownloadable; 30 | summary.isViewable = codecastSummary.isViewable; 31 | return summary; 32 | } 33 | } -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastSummaries/CodecastSummariesResponseModel.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class CodecastSummariesResponseModel 7 | { 8 | private List codecastSummaries; 9 | 10 | public CodecastSummariesResponseModel() { 11 | codecastSummaries = new ArrayList<>(); 12 | } 13 | 14 | public List getCodecastSummaries() { 15 | return codecastSummaries; 16 | } 17 | 18 | public void addCodecastSummary(CodecastSummary summary) { 19 | codecastSummaries.add(summary); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastSummaries/CodecastSummariesUseCase.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import cleancoderscom.*; 4 | import cleancoderscom.entities.Codecast; 5 | import cleancoderscom.entities.License; 6 | import cleancoderscom.entities.User; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import static cleancoderscom.entities.License.LicenseType.DOWNLOADING; 12 | import static cleancoderscom.entities.License.LicenseType.VIEWING; 13 | 14 | public class CodecastSummariesUseCase implements CodecastSummariesInputBoundary { 15 | public static boolean isLicensedFor(License.LicenseType licenseType, User user, Codecast codecast) { 16 | List licenses = Context.licenseGateway.findLicensesForUserAndCodecast(user, codecast); 17 | for (License l : licenses) { 18 | if (l.getType() == licenseType) 19 | return true; 20 | } 21 | return false; 22 | } 23 | 24 | @Override 25 | public void summarizeCodecasts(User loggedInUser, CodecastSummariesOutputBoundary presenter) 26 | { 27 | CodecastSummariesResponseModel responseModel = new CodecastSummariesResponseModel(); 28 | List allCodecasts = Context.codecastGateway.findAllCodecastsSortedChronologically(); 29 | 30 | for (Codecast codecast : allCodecasts) 31 | responseModel.addCodecastSummary(summarizeCodecast(codecast, loggedInUser)); 32 | 33 | presenter.present(responseModel); 34 | } 35 | 36 | private CodecastSummary summarizeCodecast(Codecast codecast, User user) { 37 | CodecastSummary summary = new CodecastSummary(); 38 | summary.title = codecast.getTitle(); 39 | summary.publicationDate = codecast.getPublicationDate(); 40 | summary.permalink = codecast.getPermalink(); 41 | summary.isViewable = isLicensedFor(VIEWING, user, codecast); 42 | summary.isDownloadable = isLicensedFor(DOWNLOADING, user, codecast); 43 | return summary; 44 | } 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastSummaries/CodecastSummariesView.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | public interface CodecastSummariesView 4 | { 5 | String generateView(CodecastSummariesViewModel viewModel); 6 | } 7 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastSummaries/CodecastSummariesViewImpl.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import cleancoderscom.view.ViewTemplate; 4 | 5 | import java.io.IOException; 6 | import java.util.List; 7 | 8 | public class CodecastSummariesViewImpl implements CodecastSummariesView 9 | { 10 | static String toHTML(List presentableCodecasts) { 11 | 12 | try { 13 | ViewTemplate frontPageTemplate = ViewTemplate.create("html/frontpage.html"); 14 | 15 | StringBuilder codecastLines = new StringBuilder(); 16 | for(CodecastSummariesViewModel.ViewableCodecastSummary viewableCodecastSummary : presentableCodecasts) { 17 | 18 | ViewTemplate codecastTemplate = ViewTemplate.create("html/codecast.html"); 19 | codecastTemplate.replace("title", viewableCodecastSummary.title); 20 | codecastTemplate.replace("publicationDate", viewableCodecastSummary.publicationDate); 21 | codecastTemplate.replace("permalink", viewableCodecastSummary.permalink); 22 | 23 | //staged 24 | codecastTemplate.replace("thumbnail", "https://d26o5k45lnmm4v.cloudfront.net/YmluYXJ5OjIxNzA1Nw"); 25 | codecastTemplate.replace("author", "Uncle Bob"); 26 | codecastTemplate.replace("duration", "58 min."); 27 | codecastTemplate.replace("contentActions", "Buying options go here."); 28 | 29 | codecastLines.append(codecastTemplate.getContent()); 30 | } 31 | 32 | frontPageTemplate.replace("codecasts", codecastLines.toString()); 33 | return frontPageTemplate.getContent(); 34 | } catch(IOException e) { 35 | e.printStackTrace(); 36 | return "Gunk"; 37 | } 38 | } 39 | 40 | public String generateView(CodecastSummariesViewModel viewModel) 41 | { 42 | return toHTML(viewModel.getViewableCodecasts()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastSummaries/CodecastSummariesViewModel.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class CodecastSummariesViewModel 7 | { 8 | 9 | public ArrayList viewableCodecastSummaries = new ArrayList<>(); 10 | 11 | public void addModel(ViewableCodecastSummary viewableCodecastSummary) { 12 | viewableCodecastSummaries.add(viewableCodecastSummary); 13 | } 14 | 15 | public List getViewableCodecasts() { 16 | return viewableCodecastSummaries; 17 | } 18 | 19 | public static class ViewableCodecastSummary { 20 | public String title; 21 | public String permalink; 22 | public String publicationDate; 23 | public boolean isDownloadable; 24 | public boolean isViewable; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/cleancoderscom/usecases/codecastSummaries/CodecastSummary.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import java.util.Date; 4 | 5 | public class CodecastSummary { 6 | public String title; 7 | public String permalink; 8 | public Date publicationDate; 9 | public boolean isViewable; 10 | public boolean isDownloadable; 11 | } 12 | -------------------------------------------------------------------------------- /src/cleancoderscom/view/ViewTemplate.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.view; 2 | 3 | import java.io.IOException; 4 | import java.net.URL; 5 | import java.nio.file.Files; 6 | import java.nio.file.Paths; 7 | 8 | public class ViewTemplate { 9 | private String template; 10 | 11 | public static ViewTemplate create(String templateResource) throws IOException { 12 | URL url = ClassLoader.getSystemResource(templateResource); 13 | byte[] bytes = Files.readAllBytes(Paths.get(url.getPath())); 14 | return new ViewTemplate(new String(bytes)); 15 | } 16 | 17 | public ViewTemplate(String template) { 18 | 19 | this.template = template; 20 | } 21 | 22 | public void replace(String tagName, String content) { 23 | template = template.replace("${" + tagName + "}", content); 24 | } 25 | 26 | public String getContent() { 27 | return template; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/cleancoderscom/TestSetup.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom; 2 | 3 | import cleancoderscom.*; 4 | import cleancoderscom.entities.Codecast; 5 | import cleancoderscom.entities.License; 6 | import cleancoderscom.entities.User; 7 | import cleancoderscom.doubles.InMemoryCodecastGateway; 8 | import cleancoderscom.doubles.InMemoryLicenseGateway; 9 | import cleancoderscom.doubles.InMemoryUserGateway; 10 | 11 | import java.util.Date; 12 | 13 | import static cleancoderscom.entities.License.LicenseType.*; 14 | 15 | public class TestSetup { 16 | 17 | public static void setupContext() { 18 | Context.userGateway = new InMemoryUserGateway(); 19 | Context.licenseGateway = new InMemoryLicenseGateway(); 20 | Context.codecastGateway = new InMemoryCodecastGateway(); 21 | Context.gateKeeper = new GateKeeper(); 22 | } 23 | 24 | public static void setupSampleData() { 25 | setupContext(); 26 | 27 | User bob = new User("Bob"); 28 | User micah = new User("Micah"); 29 | 30 | Context.userGateway.save(bob); 31 | Context.userGateway.save(micah); 32 | 33 | Codecast e1 = new Codecast(); 34 | e1.setTitle("Episode 1 - The Beginning"); 35 | e1.setPublicationDate(new Date()); 36 | e1.setPermalink("e1"); 37 | 38 | Codecast e2 = new Codecast(); 39 | e2.setTitle("Episode 2 - The Continuation"); 40 | e2.setPublicationDate(new Date(e1.getPublicationDate().getTime() + 1)); 41 | e2.setPermalink("e2"); 42 | 43 | Context.codecastGateway.save(e1); 44 | Context.codecastGateway.save(e2); 45 | 46 | License bobE1 = new License(VIEWING, bob, e1); 47 | License bobE2 = new License(VIEWING, bob, e2); 48 | 49 | Context.licenseGateway.save(bobE1); 50 | Context.licenseGateway.save(bobE2); 51 | 52 | Context.gateKeeper.setLoggedInUser(bob); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/cleancoderscom/doubles/GatewayUtilities.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.doubles; 2 | 3 | import cleancoderscom.entities.Entity; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.UUID; 9 | 10 | public class GatewayUtilities { 11 | private HashMap entities; 12 | 13 | public GatewayUtilities() { 14 | this.entities = new HashMap(); 15 | } 16 | 17 | public List getEntities() { 18 | List clonedEntities = new ArrayList(); 19 | for (T entity : entities.values()) 20 | addCloneToList(entity, clonedEntities); 21 | return clonedEntities; 22 | } 23 | 24 | @SuppressWarnings("unchecked") 25 | private void addCloneToList(T entity, List newEntities) { 26 | try { 27 | newEntities.add((T) entity.clone()); 28 | } catch (CloneNotSupportedException e) { 29 | throw new UnCloneableEntity(); 30 | } 31 | } 32 | 33 | public T save(T entity) { 34 | if(entity.getId() == null) 35 | entity.setId(UUID.randomUUID().toString()); 36 | String id = entity.getId(); 37 | saveCloneInMap(id, entity); 38 | return entity; 39 | } 40 | 41 | @SuppressWarnings("unchecked") 42 | private void saveCloneInMap(String id, T entity) { 43 | try { 44 | entities.put(id, (T) entity.clone()); 45 | } catch (CloneNotSupportedException e) { 46 | throw new UnCloneableEntity(); 47 | } 48 | } 49 | 50 | public void delete(T entity) { 51 | entities.remove(entity.getId()); 52 | } 53 | 54 | private static class UnCloneableEntity extends RuntimeException { 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/cleancoderscom/doubles/InMemoryCodecastGateway.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.doubles; 2 | 3 | import cleancoderscom.entities.Codecast; 4 | import cleancoderscom.gateways.CodecastGateway; 5 | 6 | import java.util.*; 7 | 8 | public class InMemoryCodecastGateway extends GatewayUtilities implements CodecastGateway { 9 | public List findAllCodecastsSortedChronologically() 10 | { 11 | List sortedCodecasts = new ArrayList(getEntities()); 12 | Collections.sort(sortedCodecasts, new Comparator() { 13 | public int compare(Codecast o1, Codecast o2) { 14 | return o1.getPublicationDate().compareTo(o2.getPublicationDate()); 15 | } 16 | }); 17 | return sortedCodecasts; 18 | } 19 | 20 | public Codecast findCodecastByTitle(String codecastTitle) { 21 | for (Codecast codecast : getEntities()) 22 | if (codecast.getTitle().equals(codecastTitle)) 23 | return codecast; 24 | return null; 25 | } 26 | 27 | @Override 28 | public Codecast findCodecastByPermalink(String permalink) { 29 | for (Codecast codecast : getEntities()) 30 | if (Objects.equals(codecast.getPermalink(), permalink)) 31 | return codecast; 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/cleancoderscom/doubles/InMemoryLicenseGateway.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.doubles; 2 | 3 | import cleancoderscom.entities.Codecast; 4 | import cleancoderscom.entities.License; 5 | import cleancoderscom.entities.User; 6 | import cleancoderscom.gateways.LicenseGateway; 7 | 8 | import java.util.*; 9 | 10 | public class InMemoryLicenseGateway extends GatewayUtilities implements LicenseGateway { 11 | public List findLicensesForUserAndCodecast(User user, Codecast codecast) { 12 | List results = new ArrayList(); 13 | for (License license : getEntities()) { 14 | if (license.getUser().isSame(user) && license.getCodecast().isSame(codecast)) 15 | results.add(license); 16 | } 17 | return results; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/cleancoderscom/doubles/InMemoryUserGateway.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.doubles; 2 | 3 | import cleancoderscom.entities.User; 4 | import cleancoderscom.gateways.UserGateway; 5 | 6 | public class InMemoryUserGateway extends GatewayUtilities implements UserGateway { 7 | public User findUserByName(String username) { 8 | for (User user : getEntities()) { 9 | if (user.getUserName().equals(username)) 10 | return user; 11 | } 12 | return null; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/cleancoderscom/entities/EntityTest.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.entities; 2 | 3 | import cleancoderscom.entities.Entity; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertFalse; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | public class EntityTest { 10 | @Test 11 | public void twoDifferentEntitysAreNotTheSame() throws Exception { 12 | Entity e1 = new Entity(); 13 | Entity e2 = new Entity(); 14 | 15 | e1.setId("e1ID"); 16 | e2.setId("e2ID"); 17 | 18 | assertFalse(e1.isSame(e2)); 19 | } 20 | 21 | @Test 22 | public void oneEntityIsTheSameAsItself() throws Exception { 23 | Entity e1 = new Entity(); 24 | e1.setId("e1ID"); 25 | 26 | assertTrue(e1.isSame(e1)); 27 | } 28 | 29 | @Test 30 | public void EntitysWithTheSameIdAreTheSame() throws Exception { 31 | Entity e1 = new Entity(); 32 | Entity e2 = new Entity(); 33 | e1.setId("e1ID"); 34 | e2.setId("e1ID"); 35 | 36 | assertTrue(e1.isSame(e2)); 37 | } 38 | 39 | @Test 40 | public void EntitysWithNullIdsAreNeverSame() throws Exception { 41 | Entity e1 = new Entity(); 42 | Entity e2 = new Entity(); 43 | 44 | assertFalse(e1.isSame(e2)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/cleancoderscom/fixtures/CodecastDetails.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.fixtures; 2 | 3 | import cleancoderscom.usecases.codecastDetails.CodecastDetailsUseCase; 4 | import cleancoderscom.Context; 5 | import cleancoderscom.usecases.codecastDetails.PresentableCodecastDetails; 6 | 7 | public class CodecastDetails { 8 | 9 | private CodecastDetailsUseCase useCase = new CodecastDetailsUseCase(); 10 | private PresentableCodecastDetails details; 11 | 12 | public boolean requestCodecast(String permalink) { 13 | details = useCase.requestCodecastDetails(Context.gateKeeper.getLoggedInUser(), permalink); 14 | return details != null; 15 | } 16 | 17 | public boolean codecastDetailsOfferPurchaseOf(String licenseType) { 18 | // return 19 | // (licenseType.equalsIgnoreCase("viewing") && !details.isViewable) || 20 | // (licenseType.equalsIgnoreCase("download") && !details.isDownloadable); 21 | return false; 22 | } 23 | 24 | public String codecastDetailsTitle() { 25 | // return details.title; 26 | return "tilt"; 27 | } 28 | 29 | public String codecastDetailsDate() { 30 | // return details.publicationDate; 31 | return "tilt"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/cleancoderscom/fixtures/CodecastPresentation.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.fixtures; 2 | 3 | import cleancoderscom.*; 4 | import cleancoderscom.entities.Codecast; 5 | import cleancoderscom.entities.License; 6 | import cleancoderscom.entities.User; 7 | import cleancoderscom.TestSetup; 8 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesPresenter; 9 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesUseCase; 10 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesViewModel; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | import static cleancoderscom.entities.License.LicenseType.DOWNLOADING; 15 | import static cleancoderscom.entities.License.LicenseType.VIEWING; 16 | 17 | public class CodecastPresentation { 18 | 19 | public CodecastPresentation() { 20 | TestSetup.setupContext(); 21 | } 22 | 23 | public static List loadViewableCodecasts() { 24 | User loggedInUser = Context.gateKeeper.getLoggedInUser(); 25 | CodecastSummariesUseCase useCase = new CodecastSummariesUseCase(); 26 | CodecastSummariesPresenter presenter = new CodecastSummariesPresenter(); 27 | useCase.summarizeCodecasts(loggedInUser, presenter); 28 | return presenter.getViewModel().getViewableCodecasts(); 29 | } 30 | 31 | public boolean addUser(String username) { 32 | Context.userGateway.save(new User(username)); 33 | return true; 34 | } 35 | 36 | public boolean loginUser(String username) { 37 | User user = Context.userGateway.findUserByName(username); 38 | if (user != null) { 39 | Context.gateKeeper.setLoggedInUser(user); 40 | return true; 41 | } else { 42 | return false; 43 | } 44 | } 45 | 46 | private boolean createLicenseForType(String username, String codecastTitle, License.LicenseType type) { 47 | User user = Context.userGateway.findUserByName(username); 48 | Codecast codecast = Context.codecastGateway.findCodecastByTitle(codecastTitle); 49 | License license = new License(type, user, codecast); 50 | Context.licenseGateway.save(license); 51 | return CodecastSummariesUseCase.isLicensedFor(type, user, codecast); 52 | } 53 | 54 | public boolean createLicenseForViewing(String username, String codecastTitle) { 55 | return createLicenseForType(username, codecastTitle, VIEWING); 56 | } 57 | 58 | public boolean createLicenseForDownloading(String username, String codecastTitle) { 59 | return createLicenseForType(username, codecastTitle, DOWNLOADING); 60 | } 61 | 62 | public String presentationUser() { 63 | return Context.gateKeeper.getLoggedInUser().getUserName(); 64 | } 65 | 66 | public boolean clearCodecasts() { 67 | List codecasts = Context.codecastGateway.findAllCodecastsSortedChronologically(); 68 | for (Codecast codecast : new ArrayList(codecasts)) { 69 | Context.codecastGateway.delete(codecast); 70 | } 71 | return Context.codecastGateway.findAllCodecastsSortedChronologically().size() == 0; 72 | } 73 | 74 | public int countOfCodecastsPresented() { 75 | return loadViewableCodecasts().size(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/cleancoderscom/fixtures/FixtureSetup.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.fixtures; 2 | 3 | import cleancoderscom.TestSetup; 4 | 5 | public class FixtureSetup { 6 | public FixtureSetup() { 7 | TestSetup.setupContext(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/cleancoderscom/fixtures/GivenCodecasts.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.fixtures; 2 | 3 | import cleancoderscom.entities.Codecast; 4 | import cleancoderscom.Context; 5 | 6 | import java.text.ParseException; 7 | import java.text.SimpleDateFormat; 8 | 9 | public class GivenCodecasts { 10 | private String title; 11 | private String publicationDate; 12 | private static SimpleDateFormat dateFormat = new SimpleDateFormat("M/d/yyyy"); 13 | private String permalink; 14 | 15 | public void setTitle(String title) { 16 | this.title = title; 17 | } 18 | 19 | public void setPublished(String publicationDate) { 20 | this.publicationDate = publicationDate; 21 | } 22 | 23 | public void setPermalink(String permalink) {this.permalink = permalink;} 24 | 25 | public void execute() throws ParseException { 26 | Codecast codecast = new Codecast(); 27 | codecast.setTitle(title); 28 | codecast.setPublicationDate(dateFormat.parse(publicationDate)); 29 | codecast.setPermalink(permalink); 30 | Context.codecastGateway.save(codecast); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /test/cleancoderscom/fixtures/OfCodeCasts.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.fixtures; 2 | 3 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesViewModel.ViewableCodecastSummary; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | //OrderedQuery 10 | public class OfCodeCasts { 11 | private List list(Object... objects) { 12 | return Arrays.asList(objects); 13 | } 14 | 15 | public List query() { 16 | final List viewableCodecasts = CodecastPresentation.loadViewableCodecasts(); 17 | List queryResponse = new ArrayList(); 18 | for (ViewableCodecastSummary summary : viewableCodecasts) 19 | queryResponse.add(makeRow(summary)); 20 | return queryResponse; 21 | 22 | } 23 | 24 | private List makeRow(ViewableCodecastSummary summary) { 25 | return list( 26 | list("title", summary.title), 27 | list("publication date", summary.publicationDate), 28 | list("picture", summary.title), 29 | list("description", summary.title), 30 | list("viewable", summary.isViewable ? "+" : "-"), 31 | list("downloadable", summary.isDownloadable ? "+" : "-")); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /test/cleancoderscom/http/RequestParserTest.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.http; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | public class RequestParserTest { 8 | 9 | @Test 10 | public void emptyRequest() throws Exception { 11 | RequestParser parser = new RequestParser(); 12 | ParsedRequest r = parser.parse(""); 13 | assertEquals("", r.method); 14 | assertEquals("", r.path); 15 | } 16 | 17 | @Test 18 | public void nullRequest() throws Exception { 19 | RequestParser parser = new RequestParser(); 20 | ParsedRequest r = parser.parse(null); 21 | assertEquals("", r.method); 22 | assertEquals("", r.path); 23 | } 24 | 25 | @Test 26 | public void requestNonEmptyRequest() throws Exception { 27 | RequestParser parser = new RequestParser(); 28 | ParsedRequest r = parser.parse("GET /foo/bar HTTP/1.1"); 29 | assertEquals("GET", r.method); 30 | assertEquals("/foo/bar", r.path); 31 | } 32 | 33 | @Test 34 | public void partialRequest() throws Exception { 35 | RequestParser parser = new RequestParser(); 36 | ParsedRequest r = parser.parse("GET"); 37 | assertEquals("GET", r.method); 38 | assertEquals("", r.path); 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test/cleancoderscom/http/RouterTest.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.http; 2 | 3 | 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | 9 | public class RouterTest { 10 | private ParsedRequest actualRequest; 11 | private Router router; 12 | 13 | @Before 14 | public void setUp() { 15 | router = new Router(); 16 | } 17 | 18 | @Test 19 | public void simplePath() throws Exception { 20 | router.addPath("it", new TestController()); 21 | ParsedRequest request = new ParsedRequest("GET", "/it"); 22 | 23 | router.route(request); 24 | 25 | assertEquals(actualRequest, request); 26 | } 27 | 28 | @Test 29 | public void pathWithDynamicData() throws Exception { 30 | router.addPath("a", new TestController()); 31 | ParsedRequest request = new ParsedRequest("GET", "/a/b/c"); 32 | 33 | router.route(request); 34 | 35 | assertEquals(actualRequest, request); 36 | } 37 | 38 | @Test 39 | public void rootPath() throws Exception { 40 | router.addPath("", new TestController()); 41 | ParsedRequest request = new ParsedRequest("GET", "/"); 42 | 43 | router.route(request); 44 | 45 | assertEquals(actualRequest, request); 46 | } 47 | 48 | class TestController implements Controller { 49 | public String handle(ParsedRequest request) { 50 | actualRequest = request; 51 | return ""; 52 | } 53 | } 54 | 55 | @Test 56 | public void Four04() throws Exception 57 | { 58 | String result = router.route(new ParsedRequest("GET", "/something-missing")); 59 | 60 | assertEquals("HTTP/1.1 404 OK\n", result); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/cleancoderscom/socketserver/SocketServerTest.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.socketserver; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.io.*; 8 | import java.net.Socket; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | public class SocketServerTest { 13 | private SocketServer server; 14 | private EchoSocketService echoService; 15 | private int port; 16 | 17 | @Before 18 | public void setup() throws Exception { 19 | port = 8042; 20 | echoService = new EchoSocketService(); 21 | server = new SocketServer(port, echoService); 22 | } 23 | 24 | @After 25 | public void tearDown() throws Exception { 26 | server.stop(); 27 | } 28 | 29 | public static abstract class TestSocketService implements SocketService { 30 | public void serve(Socket s) { 31 | try { 32 | doService(s); 33 | synchronized(this) { 34 | notify(); 35 | } 36 | s.close(); 37 | } catch(IOException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | 42 | protected abstract void doService(Socket s) throws IOException; 43 | } 44 | 45 | public static class EchoSocketService extends TestSocketService { 46 | 47 | protected void doService(Socket s) throws IOException { 48 | InputStream is = s.getInputStream(); 49 | InputStreamReader isr = new InputStreamReader(is); 50 | BufferedReader br = new BufferedReader(isr); 51 | String message = br.readLine(); 52 | OutputStream os = s.getOutputStream(); 53 | os.write(message.getBytes()); 54 | os.flush(); 55 | } 56 | } 57 | 58 | @Test 59 | public void canEcho() throws Exception { 60 | server.start(); 61 | Thread.sleep(10); 62 | Socket s = new Socket("localhost", port); 63 | OutputStream os = s.getOutputStream(); 64 | os.write("echo\n".getBytes()); 65 | String response = new BufferedReader(new InputStreamReader(s.getInputStream())).readLine(); 66 | assertEquals("echo", response); 67 | } 68 | 69 | @Test 70 | public void multipleEchos() throws Exception { 71 | server.start(); 72 | Thread.sleep(10); 73 | 74 | Socket s1 = new Socket("localhost", port); 75 | Socket s2 = new Socket("localhost", port); 76 | 77 | s1.getOutputStream().write("echo1\n".getBytes()); 78 | s2.getOutputStream().write("echo2\n".getBytes()); 79 | 80 | String response1 = new BufferedReader(new InputStreamReader(s1.getInputStream())).readLine(); 81 | String response2 = new BufferedReader(new InputStreamReader(s2.getInputStream())).readLine(); 82 | 83 | assertEquals("echo1", response1); 84 | assertEquals("echo2", response2); 85 | } 86 | } 87 | 88 | 89 | -------------------------------------------------------------------------------- /test/cleancoderscom/usecases/codecastDetails/CodecastDetailsUseCaseTest.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastDetails; 2 | 3 | import cleancoderscom.*; 4 | import cleancoderscom.entities.Codecast; 5 | import cleancoderscom.entities.User; 6 | import cleancoderscom.TestSetup; 7 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesPresenter; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | 13 | public class CodecastDetailsUseCaseTest { 14 | 15 | private User user; 16 | 17 | @Before 18 | public void setup() 19 | { 20 | TestSetup.setupContext(); 21 | user = Context.userGateway.save(new User("User")); 22 | } 23 | 24 | // TODO: 11/3/15 Start here. Get this passing. 25 | // @Test 26 | // public void createsCodecastDetailsPresentation() throws Exception { 27 | // Codecast codecast = new Codecast(); 28 | // codecast.setTitle("Codecast"); 29 | // codecast.setPermalink("permalink-a"); 30 | // codecast.setPublicationDate(CodecastSummariesPresenter.dateFormat.parse("1/2/2015")); 31 | // Context.codecastGateway.save(codecast); 32 | // 33 | // CodecastDetailsUseCase userCase = new CodecastDetailsUseCase(); 34 | // PresentableCodecastDetails details = userCase.requestCodecastDetails(user, "permalink-a"); 35 | // 36 | // assertEquals("Codecast", details.title); 37 | // assertEquals("1/02/2015", details.publicationDate); 38 | // } 39 | 40 | @Test 41 | public void doesntCrashOnMissingCodecast() throws Exception { 42 | CodecastDetailsUseCase userCase = new CodecastDetailsUseCase(); 43 | PresentableCodecastDetails details = userCase.requestCodecastDetails(user, "missing"); 44 | 45 | assertEquals(false, details.wasFound); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/cleancoderscom/usecases/codecastSummaries/CodecastSummariesControllerTest.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import cleancoderscom.Context; 4 | import cleancoderscom.TestSetup; 5 | import cleancoderscom.http.ParsedRequest; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | public class CodecastSummariesControllerTest { 12 | 13 | public CodecastSummariesInputBoundarySpy useCaseSpy; 14 | public CodecastSummariesOutputBoundarySpy presenterSpy; 15 | public CodecastSummariesViewSpy viewSpy; 16 | public CodecastSummariesController controller; 17 | 18 | @Before 19 | public void setUp() 20 | { 21 | TestSetup.setupSampleData(); 22 | 23 | useCaseSpy = getCodecastSummariesInputBoundarySpy(); 24 | presenterSpy = new CodecastSummariesOutputBoundarySpy(); 25 | viewSpy = new CodecastSummariesViewSpy(); 26 | controller = new CodecastSummariesController(useCaseSpy, presenterSpy, viewSpy); 27 | } 28 | 29 | @Test 30 | public void testInputBoundaryInvocation() throws Exception 31 | { 32 | ParsedRequest request = new ParsedRequest("GET", "blah"); 33 | controller.handle(request); 34 | 35 | assertTrue(useCaseSpy.summarizeCodecastsWasCalled); 36 | String loggedInUser = Context.userGateway.findUserByName("Bob").getId(); 37 | assertEquals(loggedInUser, useCaseSpy.requestedUser.getId()); 38 | assertSame(presenterSpy, useCaseSpy.outputBoundary); 39 | } 40 | 41 | @Test 42 | public void controllerSendsTheViewModelToTheView() throws Exception 43 | { 44 | presenterSpy.viewModel = new CodecastSummariesViewModel(); 45 | 46 | ParsedRequest request = new ParsedRequest("GET", "blah"); 47 | controller.handle(request); 48 | 49 | assertTrue(viewSpy.generateViewWasCalled); 50 | assertSame(presenterSpy.viewModel, viewSpy.viewModel); 51 | } 52 | 53 | private CodecastSummariesInputBoundarySpy getCodecastSummariesInputBoundarySpy() 54 | { 55 | return new CodecastSummariesInputBoundarySpy(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /test/cleancoderscom/usecases/codecastSummaries/CodecastSummariesInputBoundarySpy.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import cleancoderscom.entities.User; 4 | 5 | public class CodecastSummariesInputBoundarySpy implements CodecastSummariesInputBoundary 6 | { 7 | public boolean summarizeCodecastsWasCalled = false; 8 | public User requestedUser; 9 | public CodecastSummariesOutputBoundary outputBoundary; 10 | 11 | public void summarizeCodecasts(User loggedInUser, CodecastSummariesOutputBoundary presenter) { 12 | summarizeCodecastsWasCalled = true; 13 | requestedUser = loggedInUser; 14 | outputBoundary = presenter; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/cleancoderscom/usecases/codecastSummaries/CodecastSummariesOutputBoundarySpy.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | public class CodecastSummariesOutputBoundarySpy implements CodecastSummariesOutputBoundary 4 | { 5 | public CodecastSummariesViewModel viewModel; 6 | public CodecastSummariesResponseModel responseModel; 7 | 8 | @Override 9 | public CodecastSummariesViewModel getViewModel() 10 | { 11 | return viewModel; 12 | } 13 | 14 | @Override 15 | public void present(CodecastSummariesResponseModel responseModel) { 16 | this.responseModel = responseModel; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/cleancoderscom/usecases/codecastSummaries/CodecastSummariesPresenterTest.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesViewModel.ViewableCodecastSummary; 4 | import org.junit.Test; 5 | 6 | import java.util.GregorianCalendar; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | public class CodecastSummariesPresenterTest { 11 | 12 | @Test 13 | public void validateViewModel() throws Exception 14 | { 15 | CodecastSummariesResponseModel responseModel = new CodecastSummariesResponseModel(); 16 | CodecastSummary summary = new CodecastSummary(); 17 | summary.title = "Title"; 18 | summary.publicationDate = new GregorianCalendar(2015, 10, 3).getTime(); 19 | summary.permalink = "permalink"; 20 | summary.isViewable = true; 21 | summary.isDownloadable = false; 22 | responseModel.addCodecastSummary(summary); 23 | 24 | CodecastSummariesPresenter presenter = new CodecastSummariesPresenter(); 25 | presenter.present(responseModel); 26 | 27 | CodecastSummariesViewModel viewModel = presenter.getViewModel(); 28 | ViewableCodecastSummary viewableSummary = viewModel.viewableCodecastSummaries.get(0); 29 | assertEquals(summary.title, viewableSummary.title); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /test/cleancoderscom/usecases/codecastSummaries/CodecastSummariesUseCaseTest.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | import cleancoderscom.*; 4 | import cleancoderscom.entities.Codecast; 5 | import cleancoderscom.entities.License; 6 | import cleancoderscom.entities.User; 7 | import cleancoderscom.TestSetup; 8 | import de.bechte.junit.runners.context.HierarchicalContextRunner; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | 13 | import java.util.Date; 14 | import java.util.GregorianCalendar; 15 | import java.util.List; 16 | 17 | import static cleancoderscom.entities.License.LicenseType.DOWNLOADING; 18 | import static cleancoderscom.entities.License.LicenseType.VIEWING; 19 | import static org.junit.Assert.*; 20 | 21 | @RunWith(HierarchicalContextRunner.class) 22 | public class CodecastSummariesUseCaseTest { 23 | private User user; 24 | private CodecastSummariesUseCase useCase; 25 | public CodecastSummariesOutputBoundarySpy presenterSpy; 26 | 27 | @Before 28 | public void setUp() { 29 | TestSetup.setupContext(); 30 | user = Context.userGateway.save(new User("User")); 31 | useCase = new CodecastSummariesUseCase(); 32 | presenterSpy = new CodecastSummariesOutputBoundarySpy(); 33 | 34 | } 35 | 36 | @Test 37 | public void useCaseWiring() throws Exception 38 | { 39 | useCase.summarizeCodecasts(user, presenterSpy); 40 | assertNotNull(presenterSpy.responseModel); 41 | } 42 | 43 | public class GivenNoCodecasts { 44 | @Test 45 | public void noneArePresented() throws Exception { 46 | useCase.summarizeCodecasts(user, presenterSpy); 47 | 48 | assertEquals(0, presenterSpy.responseModel.getCodecastSummaries().size()); 49 | } 50 | } 51 | 52 | public class GivenOneCodecast { 53 | private Codecast codecast; 54 | 55 | @Before 56 | public void setupCodecast() { 57 | codecast = Context.codecastGateway.save(new Codecast()); 58 | } 59 | 60 | @Test 61 | public void oneIsPresented() throws Exception { 62 | codecast.setTitle("Some Title"); 63 | Date now = new GregorianCalendar(2014, 4, 19).getTime(); 64 | codecast.setPublicationDate(now); 65 | codecast.setPermalink("permalink"); 66 | Context.codecastGateway.save(codecast); 67 | presenterSpy = new CodecastSummariesOutputBoundarySpy(); 68 | 69 | useCase.summarizeCodecasts(user, presenterSpy); 70 | 71 | assertEquals(1, presenterSpy.responseModel.getCodecastSummaries().size()); 72 | CodecastSummary codecastSummary = presenterSpy.responseModel.getCodecastSummaries().get(0); 73 | assertEquals("Some Title", codecastSummary.title); 74 | assertEquals(now, codecastSummary.publicationDate); 75 | assertEquals("permalink", codecastSummary.permalink); 76 | } 77 | 78 | public class GivenNoLicenses { 79 | @Test 80 | public void userCannotViewCodecast() throws Exception { 81 | assertFalse(useCase.isLicensedFor(VIEWING, user, codecast)); 82 | } 83 | 84 | @Test 85 | public void presentedCodecastShowsNotViewable() throws Exception { 86 | useCase.summarizeCodecasts(user, presenterSpy); 87 | CodecastSummary summary = presenterSpy.responseModel.getCodecastSummaries().get(0); 88 | assertFalse(summary.isViewable); 89 | } 90 | } 91 | 92 | public class GivenOneViewingLicenseForTheUser { 93 | private License viewLicense; 94 | 95 | @Before 96 | public void setupLicense() { 97 | viewLicense = new License(VIEWING, user, codecast); 98 | Context.licenseGateway.save(viewLicense); 99 | } 100 | 101 | @Test 102 | public void userCanViewCodecast() throws Exception { 103 | assertTrue(useCase.isLicensedFor(VIEWING, user, codecast)); 104 | } 105 | 106 | @Test 107 | public void unlicensedUserCannotViewOtherUsersCodecast() throws Exception { 108 | User otherUser = new User("otherUser"); 109 | Context.userGateway.save(otherUser); 110 | assertFalse(useCase.isLicensedFor(VIEWING, otherUser, codecast)); 111 | } 112 | 113 | @Test 114 | public void presentedCodecastIsViewable() throws Exception { 115 | Context.licenseGateway.save(new License(VIEWING, user, codecast)); 116 | useCase.summarizeCodecasts(user, presenterSpy); 117 | CodecastSummary summary = presenterSpy.responseModel.getCodecastSummaries().get(0); 118 | assertTrue(summary.isViewable); 119 | } 120 | } 121 | 122 | public class GivenOneDownloadLicenseForTheUser { 123 | private License downloadLicense; 124 | 125 | @Before 126 | public void setupDownloadLicense() { 127 | downloadLicense = new License(DOWNLOADING, user, codecast); 128 | Context.licenseGateway.save(downloadLicense); 129 | } 130 | 131 | @Test 132 | public void presentedCodecastIsDownloadable() throws Exception { 133 | useCase.summarizeCodecasts(user, presenterSpy); 134 | CodecastSummary summary = presenterSpy.responseModel.getCodecastSummaries().get(0); 135 | assertTrue(summary.isDownloadable); 136 | assertFalse(summary.isViewable); 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /test/cleancoderscom/usecases/codecastSummaries/CodecastSummariesViewSpy.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.usecases.codecastSummaries; 2 | 3 | public class CodecastSummariesViewSpy implements CodecastSummariesView 4 | { 5 | public boolean generateViewWasCalled = false; 6 | public CodecastSummariesViewModel viewModel; 7 | 8 | public String generateView(CodecastSummariesViewModel viewModel) 9 | { 10 | this.viewModel = viewModel; 11 | generateViewWasCalled = true; 12 | return null; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/cleancoderscom/utilities/HelloWorld.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.utilities; 2 | 3 | import cleancoderscom.socketserver.SocketServer; 4 | import cleancoderscom.socketserver.SocketService; 5 | 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | import java.net.Socket; 9 | 10 | public class HelloWorld implements SocketService { 11 | public static void main(String[] args) throws Exception { 12 | SocketServer server = new SocketServer(8080, new HelloWorld()); 13 | server.start(); 14 | } 15 | 16 | @Override 17 | public void serve(Socket s) { 18 | try { 19 | OutputStream os = s.getOutputStream(); 20 | 21 | String response = "HTTP/1.1 200 OK\n" + 22 | "Content-Length: 21\n" + 23 | "\n" + 24 | "

    Hello, world

    "; 25 | 26 | os.write(response.getBytes()); 27 | } catch(IOException e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/cleancoderscom/utilities/Main.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.utilities; 2 | 3 | import cleancoderscom.http.ParsedRequest; 4 | import cleancoderscom.http.RequestParser; 5 | import cleancoderscom.http.Router; 6 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesController; 7 | import cleancoderscom.socketserver.SocketServer; 8 | import cleancoderscom.TestSetup; 9 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesPresenter; 10 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesUseCase; 11 | import cleancoderscom.usecases.codecastSummaries.CodecastSummariesViewImpl; 12 | 13 | import java.io.BufferedReader; 14 | import java.io.IOException; 15 | import java.io.InputStreamReader; 16 | 17 | 18 | public class Main { 19 | 20 | public static void main(String[] args) throws Exception { 21 | Router router = new Router(); 22 | CodecastSummariesUseCase useCase = new CodecastSummariesUseCase(); 23 | CodecastSummariesPresenter presenter = new CodecastSummariesPresenter(); 24 | CodecastSummariesViewImpl view = new CodecastSummariesViewImpl(); 25 | router.addPath("", new CodecastSummariesController(useCase, presenter, view)); 26 | // TODO - Add this Controller 27 | // router.addPath("episode", new CodecastDetailsController()); 28 | 29 | 30 | TestSetup.setupSampleData(); 31 | SocketServer server = new SocketServer(8888, s -> { 32 | try { 33 | BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream())); 34 | ParsedRequest request = new RequestParser().parse(reader.readLine()); 35 | String response = router.route(request); 36 | 37 | s.getOutputStream().write(response.getBytes()); 38 | 39 | s.close(); 40 | } catch(IOException e) { 41 | e.printStackTrace(); 42 | } 43 | }); 44 | server.start(); 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /test/cleancoderscom/view/ViewTemplateTest.java: -------------------------------------------------------------------------------- 1 | package cleancoderscom.view; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | public class ViewTemplateTest { 8 | 9 | @Test 10 | public void noReplacement() throws Exception { 11 | ViewTemplate template = new ViewTemplate("some static content"); 12 | 13 | assertEquals("some static content", template.getContent()); 14 | } 15 | 16 | @Test 17 | public void simpleReplacement() throws Exception { 18 | ViewTemplate template = new ViewTemplate("replace ${this}."); 19 | template.replace("this", "replacement"); 20 | assertEquals("replace replacement.", template.getContent()); 21 | } 22 | 23 | 24 | 25 | } 26 | --------------------------------------------------------------------------------