├── .gitignore ├── LICENSE ├── README.en.md ├── README.md ├── chapters ├── en │ └── chapter1.md └── es │ ├── chapter1.md │ ├── chapter2.md │ ├── chapter3.md │ └── chapter4.md └── samples ├── Chapter1 └── FooApi │ ├── FooApi.sln │ ├── FooApi │ ├── Controllers │ │ └── FooController.cs │ ├── FooApi.csproj │ ├── FooConfiguration.cs │ └── Models │ │ └── Bar.cs │ ├── FunctionalTests │ ├── FunctionalTests.csproj │ ├── Scenarios │ │ └── FooController.cs │ └── Seedwork │ │ └── FooFixture.cs │ └── Host │ ├── Host.csproj │ ├── Program.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ └── wwwroot │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── images │ ├── banner1.svg │ ├── banner2.svg │ ├── banner3.svg │ └── banner4.svg │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── .bower.json │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js │ ├── jquery-validation-unobtrusive │ ├── .bower.json │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── .bower.json │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── .bower.json │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── Chapter2 └── FooApi │ ├── FooApi.sln │ ├── FooApi │ ├── Controllers │ │ └── FooController.cs │ ├── FooApi.csproj │ ├── FooConfiguration.cs │ └── Models │ │ └── Bar.cs │ ├── FunctionalTests │ ├── FunctionalTests.csproj │ ├── Scenarios │ │ └── FooController.cs │ └── Seedwork │ │ └── FooFixture.cs │ └── Host │ ├── Host.csproj │ ├── Program.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ └── wwwroot │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── images │ ├── banner1.svg │ ├── banner2.svg │ ├── banner3.svg │ └── banner4.svg │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── .bower.json │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js │ ├── jquery-validation-unobtrusive │ ├── .bower.json │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── .bower.json │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── .bower.json │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── Chapter3 └── FooApi │ ├── FooApi.sln │ ├── FooApi │ ├── Controllers │ │ └── FooController.cs │ ├── FooApi.csproj │ ├── FooConfiguration.cs │ └── Models │ │ └── Bar.cs │ ├── FunctionalTests │ ├── FunctionalTests.csproj │ ├── Scenarios │ │ └── FooController.cs │ └── Seedwork │ │ ├── FooFixture.cs │ │ └── MyTestsAuthenticationHandler.cs │ └── Host │ ├── Host.csproj │ ├── Program.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ └── wwwroot │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── images │ ├── banner1.svg │ ├── banner2.svg │ ├── banner3.svg │ └── banner4.svg │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── .bower.json │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js │ ├── jquery-validation-unobtrusive │ ├── .bower.json │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── .bower.json │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── .bower.json │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map └── Chapter4 └── FooApi ├── FooApi.sln ├── FooApi ├── Controllers │ └── FooController.cs ├── FooApi.csproj ├── FooConfiguration.cs ├── IWebHostExtensions.cs ├── Migrations │ ├── 20180307192205_Initial.Designer.cs │ ├── 20180307192205_Initial.cs │ └── FooDbContextModelSnapshot.cs └── Models │ ├── Bar.cs │ └── FooDbContext.cs ├── FunctionalTests ├── FunctionalTests.csproj ├── Scenarios │ └── FooController.cs └── Seedwork │ ├── FooFixture.cs │ ├── MyTestsAuthenticationHandler.cs │ └── ResetDatabaseAttribute.cs └── Host ├── Host.csproj ├── Program.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json ├── bundleconfig.json └── wwwroot ├── css ├── site.css └── site.min.css ├── favicon.ico ├── images ├── banner1.svg ├── banner2.svg ├── banner3.svg └── banner4.svg ├── js ├── site.js └── site.min.js └── lib ├── bootstrap ├── .bower.json ├── LICENSE └── dist │ ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap-theme.min.css.map │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ └── bootstrap.min.css.map │ ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 │ └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ └── npm.js ├── jquery-validation-unobtrusive ├── .bower.json ├── jquery.validate.unobtrusive.js └── jquery.validate.unobtrusive.min.js ├── jquery-validation ├── .bower.json ├── LICENSE.md └── dist │ ├── additional-methods.js │ ├── additional-methods.min.js │ ├── jquery.validate.js │ └── jquery.validate.min.js └── jquery ├── .bower.json ├── LICENSE.txt └── dist ├── jquery.js ├── jquery.min.js └── jquery.min.map /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Xabaril 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # Effective testing of our HTTP APIs in .NET Core 2 | 3 | When we decided to write about "Effective Testing of our HTTP APIs in .NET Core" we did not think about writing in depth about it. It is probably that with some blog entries we would have managed to share our ideas and extend a clear message across the readers. But in the end, the desire to share experiences and different solutions to common problems in our day to day, seems to take up a bit more than we thought. Therefore, we built this short manual, which we hope will serve to introduce you to the path of **functional testing** of our "HTTP APIs built with .NET Core 2.X". 4 | 5 | ## Introduction 6 | 7 | You may have asked yourself, "Why functional tests on our HTTP API if I already have unit tests?" Well, the answer is quite simple, "because they are complementary". That is, having unit tests in our software developments does not prevent us from doing this kind of functional tests. Unit tests are fast and allow us to test different casuistics of our components in an isolated way. 8 | 9 | On the contrary, the functional tests presented here are not so fast, but they allow us to test together the different components or parts that make up our solution and they end up beign a good indicator of code coverage. 10 | 11 | Until recently, running these tests involved some work to make them be part of the normal development cycle of a programmer (coding, construction and tests). With the arrival of *TestServer* (available in both .NET Core and Full Framework with Web Api 2.X) this process has been simplified and we have the possibility to run this type of tests similarly to unit tests and therefore included them within the development flow that we usually follows. 12 | 13 | ## Content 14 | 15 | The content of this manual is open and we will try to keep it as up-to-date as possible. Something that of course is not always easy for the delivery cycles of new features that we have in **.NET Core** project. In principle, we will be based on **.NET Core 2.X** specifically version **2.0**, the latest released version when this manual was being written. 16 | 17 | ## Table of Contents 18 | 19 | 1. [Introduction to TestServer](chapters/en/chapter1.md) 20 | 2. [Routes and parameters](chapters/en/chapter2.md) 21 | 3. [Authorization](chapters/en/chapter3.md) 22 | 4. [Working with data](chapters/en/chapter4.md) 23 | 24 | Each **chapter** has the **code** associated with the explanation of it. You can find them in the **samples** folder. 25 | 26 | ## Acknowledgements 27 | 28 | To all the people who have contributed to the writing and revision of this manual, especially to: 29 | 30 | 1. Unai Zorrilla Castro 31 | 2. Luis Ruiz Pavon 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pruebas efectivas de nuestras HTTP APIs en .NET Core 2 | 3 | Cuando decidimos escribir acerca "Testing efectivo de HTTP API en .NET Core" no teníamos pensado extendernos mucho. Es probable que con algunas entradas de blog hubieramos conseguido compartir nuestras ideas y trasmitir el mensaje de una forma relativamente clara. Pero al final, las ganas de compartir experiencias y diferentes soluciones a problemas comunes en nuestro día a día parece que ocupa mucho más de lo que nosotros pensabamos. Por eso, construimos este pequeño manual, que esperemos os sirva para introduciros en el camino del **testing funcional** de nuestras "HTTP APIs construidas con .NET Core 2.X". 4 | 5 | ## Introducción 6 | 7 | Es probable que usted al leer esto se pregunte, "¿Porqué pruebas funcionales sobre nuestro HTTP API si ya tengo tests unitarios?" Pues bien, la respuesta es sencilla, porque son complementarias. Es decir, tener pruebas unitarias en nuestros desarrollos de software en este caso desarrollo de HTTP API, no impide que hagamos este tipo de pruebas funcionales. Las pruebas unitarias, son aisladas y muy rápidas (o así deberían ser) y nos permiten probar diferentes casuísticas de nuestros componentes de forma aislada. 8 | 9 | Por el contrario, las pruebas funcionales como aquí presentamos no son tan rápidas pero nos permiten probar en conjunto los diferentes componentes que forman nuestra solución, dándonos además un buen índice de cobertura de código. 10 | 11 | Hasta hace relativamente poco, ejecutar estas pruebas implicaba un cierto trabajo de despliegue, haciendo que las mismas no formaran finalmente parte del ciclo normal de un desarrollador (código, construcción y pruebas) por el setup requerido para ejecutarlas. Con la llegada de *TestServer* (*recuerde que no solamente está disponible en .NET Core sino también en Full FX con Web Api 2.X*), este proceso se ha simplificado y tenemos ya la posibilidad de correr este tipo de pruebas como si de pruebas unitarias se tratara y por lo tanto incluirlas dentro de ese flujo de desarrollo que todos solemos seguir. 12 | 13 | ## Contenido 14 | 15 | El contenido de este manual es abierto y trataremos de que esté siempre lo más actualizado posible. Algo que por supuesto no siempre es sencillo por los ciclos de entrega de nuevas características que tenemos en **.NET Core**. En principio, nos basaremos en **.NET Core 2.X**, en concreto con la versión **2.0**, la última versión release cuando este manual se estaba escribiendo. 16 | 17 | ## Índice en castellano 18 | 19 | 1. [Introducción a TestServer](chapters/es/chapter1.md) 20 | 2. [Rutas y parámetros](chapters/es/chapter2.md) 21 | 3. [Autorización](chapters/es/chapter3.md) 22 | 4. [Trabajando con datos](chapters/es/chapter4.md) 23 | 24 | ## English Index 25 | 26 | 1. [Introduction to TestServer](chapters/en/chapter1.md) 27 | 28 | 29 | Cada capítulo lleva asociado el código relativo a la explicación del mismo. Puede encontrarlos dentro de la carpeta **samples**. 30 | 31 | ## Agradecimientos 32 | 33 | A todas las personas que han contribuido a la escritura y revisión de este manual: 34 | 35 | 1. Unai Zorrilla Castro (@unaizorrilla) 36 | 2. Luis Ruiz Pavon (@lurumad) 37 | 3. Jorge Rodriguez Galán (@jrgcubano) 38 | -------------------------------------------------------------------------------- /chapters/es/chapter2.md: -------------------------------------------------------------------------------- 1 | 2 | # Rutas y parámetros 3 | 4 | Si ha llegado hasta aquí ya tendrá un esqueleto fundamental sobre el que iremos profundizando. Con el fin de ir trabajando sobre algo cada vez mas similar a lo que se encontrará en sus proyectos reales iremos ampliando el ejemplo base agregando nuevas funcionalidades en las que iremos tratando diferentes casuísticas. En este caso hablaremos sobre como gestionar las rutas de nuestros controladores en nuestros tests y como trabajar con los parámetros asociados a las diferentes acciones a las que invoquemos. 5 | 6 | Para ilustrar los diferentes elementos vamos a aumentar el código de nuestro HTTP API, con el fin de que podamos reflejar diferentes elementos. Los cambios introducidos implican agregar los siguienes paquetes a nusetro *FooApi*. 7 | 8 | ```PowerShell 9 | Install-Package Microsoft.AspNetCore.Mvc.Versioning 10 | ``` 11 | 12 | ```csharp 13 | [Route("api/v{version:apiVersion}/[controller]")] 14 | public class FooController 15 | :Controller 16 | { 17 | [HttpGet()] 18 | public IActionResult Get(int id) 19 | { 20 | var bar = new Bar() { Id = id }; 21 | 22 | return Ok(bar); 23 | } 24 | 25 | [HttpPost()] 26 | public IActionResult Post([FromBody]Bar bar) 27 | { 28 | return CreatedAtAction(nameof(Get), new { id = bar.Id }); 29 | } 30 | } 31 | ``` 32 | 33 | Al código de partida hemos agregado un nuevo método asi como un cambio en la ruta para reflejar versionado de nuestro API. Con estos cambios nso vamos a nuestros tests y podemos reescribirlos tambien como sigue: 34 | 35 | ```csharp 36 | [Collection("Foo")] 37 | public class foo_api_should 38 | { 39 | private readonly FooFixture Given; 40 | 41 | public foo_api_should(FooFixture fooFixture) 42 | { 43 | Given = fooFixture; 44 | } 45 | 46 | [Fact] 47 | public async Task get_bar_when_requested() 48 | { 49 | var response = await Given.FooServer.CreateRequest("api/v1/foo?id=1") 50 | .GetAsync(); 51 | 52 | response.EnsureSuccessStatusCode(); 53 | } 54 | 55 | [Fact] 56 | public async Task post_new_bar() 57 | { 58 | var response = await Given.FooServer.CreateRequest("api/v1/foo") 59 | .And(message => 60 | { 61 | var content = JsonConvert.SerializeObject(new Bar() { Id = 1 }); 62 | message.Content = new StringContent(content,Encoding.UTF8,"application/json"); 63 | 64 | }).PostAsync(); 65 | 66 | response.EnsureSuccessStatusCode(); 67 | } 68 | } 69 | ``` 70 | 71 | Como puede observar, ahora que ya tenemos un par de test simples ya hay algunas cosas que nos hacen ver que tendremos algunos *code smells*. El primero de ellos se refiere al propio uso de las rutas, en ambos tests estamos repitiendo la ruta utilizada *api/v1/foo* y eso nos puede implicar como se imaginará problemas con en cualquier otro sitio donde tengamos magic strings y código repetido. 72 | 73 | Un primer intento para arreglar esto suele basarse en la creación de una pequeña clase dónde podamos gestionar estas rutas, como podria ser la siguiente, que por supuesto tambien es susceptible de ser mejorada: 74 | 75 | ```csharp 76 | static class FooAPI 77 | { 78 | static string BASEURI = "api/v1/foo"; 79 | 80 | public static class Get 81 | { 82 | public static string Bar(int id) 83 | { 84 | return $"{BASEURI}?id={id}"; 85 | } 86 | } 87 | 88 | public static class Post 89 | { 90 | public static string Bar() 91 | { 92 | return BASEURI; 93 | } 94 | } 95 | } 96 | ``` 97 | 98 | Ahora en nuestros tests, eliminaremos las *magic strings* en favor del uso de estas clases: 99 | 100 | ```csharp 101 | [Fact] 102 | public async Task get_bar_when_requested() 103 | { 104 | var response = await Given.FooServer.CreateRequest(FooAPI.Get.Bar(1)) 105 | .GetAsync(); 106 | 107 | response.EnsureSuccessStatusCode(); 108 | } 109 | 110 | [Fact] 111 | public async Task post_new_bar() 112 | { 113 | var response = await Given.FooServer.CreateRequest(FooAPI.Post.Bar()) 114 | .And(message => 115 | { 116 | var content = JsonConvert.SerializeObject(new Bar() { Id = 1 }); 117 | message.Content = new StringContent(content,Encoding.UTF8,"application/json"); 118 | 119 | }).PostAsync(); 120 | 121 | response.EnsureSuccessStatusCode(); 122 | } 123 | ``` 124 | Otro de los elementos con el que nos encontramos es que en todos los métodos donde estemos pasando tipos complejos como es el caso de nuestro **Bar** tendremos que andar repitiendo el trabajo de serialización. Esto al igual que hemos hecho con las rutas tambien podria refactorizarse usando Builders y disponiéndolos en nuestro *Given*, no obstante intentaremos presentar el uso de una librería,*Acheve.TestHost*, que nos podria ayudar a simplificar nuestros tests. Instalaremos por lo tanto esta librería en nuestro proyecto de tests 125 | 126 | 127 | ```PowerShell 128 | Install-Package Acheve.TestHost 129 | ``` 130 | 131 | Esta librería nos aporta un nuevo método de extensión para nuestro *TestServer* llamado *CreateHttpApiRequest* gracias al cual podremos tener nuestros tests anteriores de una forma mas simple. En este momento *CreateHttpApiRequest* solamente es válido para HTTP API usando Attribute Routing, por lo tanto no funcionará de forma correcta para API HTTP que se basen en *conventional routes*. Ahora podemos utilizar este método *CreateHttpApiRequest* y librarnos de tener que conocer las rutas y como los difernetes elementos se mapean a los diferentes segmentos que podamos tener en las mismas asi como el trabajo de serialización de nuestros objetos puesto que el se encarga de todos estos aspectos. 132 | 133 | ```csharp 134 | [Fact] 135 | public async Task get_bar_when_requested() 136 | { 137 | var response = await Given.FooServer 138 | .CreateHttpApiRequest(foo=>foo.Get(1),new { version = 1 }) 139 | .GetAsync(); 140 | 141 | response.EnsureSuccessStatusCode(); 142 | } 143 | 144 | [Fact] 145 | public async Task post_new_bar() 146 | { 147 | var bar = new Bar() { Id = 1 }; 148 | var response = await Given.FooServer 149 | .CreateHttpApiRequest(foo => foo.Post(bar), new { version = 1 }) 150 | .PostAsync(); 151 | 152 | response.EnsureSuccessStatusCode(); 153 | } 154 | ``` 155 | 156 | Fíjese como se usa el tipo anónimo para especificar aquellos *tokens* que no pueden ser obtenidos de la propia definición de la acción llamada en nuestro controlador. 157 | 158 | # Conclusiones 159 | 160 | En este capítulo hemos visto como enfrentarnos al manejo de las diferentes rutas de nuestro controladores en los tests de nuestras HTTP API para simplificar nuestros tests e intentar luchar contra diferentes *code smell* que nos pueden aparecer. 161 | 162 | > [Continuar la lectura](./chapter3.md) -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/FooApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FooApi", "FooApi\FooApi.csproj", "{B6308A12-C556-4A53-9F94-AB43424D2EB7}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "Host\Host.csproj", "{1B7B0FF8-C0AE-4482-80E3-B9E190440E92}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionalTests", "FunctionalTests\FunctionalTests.csproj", "{4CF7F430-B5E9-46BA-8802-37D3AF425BF2}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {2C32CA1E-766F-4927-B9CB-DFC7ECE759D1} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/FooApi/Controllers/FooController.cs: -------------------------------------------------------------------------------- 1 | using FooApi.Models; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace FooApi.Controllers 5 | { 6 | [Route("api/[controller]")] 7 | public class FooController : Controller 8 | { 9 | [HttpGet("")] 10 | public IActionResult Get(int id) 11 | { 12 | var bar = new Bar() { Id = id }; 13 | 14 | return Ok(bar); 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/FooApi/FooApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/FooApi/FooConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace FooApi 8 | { 9 | public static class FooConfiguration 10 | { 11 | public static IServiceCollection ConfigureServices(IServiceCollection services) => 12 | services 13 | .AddMvc() 14 | .Services; 15 | 16 | public static void Configure(IApplicationBuilder app, Func configureHost) => 17 | configureHost(app) 18 | .UseMvc(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/FooApi/Models/Bar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FooApi.Models 6 | { 7 | public class Bar 8 | { 9 | public int Id { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/FunctionalTests/FunctionalTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/FunctionalTests/Scenarios/FooController.cs: -------------------------------------------------------------------------------- 1 | using FooApi; 2 | using FunctionalTests.Seedwork; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.TestHost; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | 10 | namespace FunctionalTests.Scenarios 11 | { 12 | [Collection("Foo")] 13 | public class foo_api_should 14 | { 15 | private readonly FooFixture Given; 16 | 17 | public foo_api_should(FooFixture fooFixture) 18 | { 19 | Given = fooFixture; 20 | } 21 | 22 | [Fact] 23 | public async Task get_bar_when_requested() 24 | { 25 | var response = await Given.FooServer.CreateRequest("api/foo") 26 | .GetAsync(); 27 | 28 | response.EnsureSuccessStatusCode(); 29 | } 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/FunctionalTests/Seedwork/FooFixture.cs: -------------------------------------------------------------------------------- 1 | using FooApi; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.TestHost; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | using Xunit; 10 | 11 | namespace FunctionalTests.Seedwork 12 | { 13 | public class FooFixture 14 | { 15 | public TestServer FooServer { get; private set; } 16 | 17 | public FooFixture() 18 | { 19 | var hostBuilder = new WebHostBuilder() 20 | .UseStartup(); 21 | 22 | FooServer = new TestServer(hostBuilder); 23 | } 24 | } 25 | 26 | [CollectionDefinition("Foo")] 27 | public class FooFixtureCollection : ICollectionFixture 28 | { 29 | } 30 | 31 | class TestStartup 32 | { 33 | public void ConfigureServices(IServiceCollection services) 34 | { 35 | FooConfiguration.ConfigureServices(services); 36 | } 37 | 38 | public void Configure(IApplicationBuilder app) 39 | { 40 | FooConfiguration.Configure(app, host => host); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/Host.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace Host 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | BuildWebHost(args).Run(); 18 | } 19 | 20 | public static IWebHost BuildWebHost(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup() 23 | .Build(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using FooApi; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | 11 | namespace Host 12 | { 13 | public class Startup 14 | { 15 | public Startup(IConfiguration configuration) 16 | { 17 | Configuration = configuration; 18 | } 19 | 20 | public IConfiguration Configuration { get; } 21 | 22 | // This method gets called by the runtime. Use this method to add services to the container. 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | FooConfiguration 26 | .ConfigureServices(services); 27 | } 28 | 29 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 30 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 31 | { 32 | FooConfiguration.Configure(app, host => 33 | { 34 | if (env.IsDevelopment()) 35 | { 36 | host.UseBrowserLink(); 37 | host.UseDeveloperExceptionPage(); 38 | } 39 | else 40 | { 41 | host.UseExceptionHandler("/Home/Error"); 42 | } 43 | 44 | host.UseStaticFiles(); 45 | 46 | return host; 47 | }); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/bundleconfig.json: -------------------------------------------------------------------------------- 1 | // Configure bundling and minification for the project. 2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241 3 | [ 4 | { 5 | "outputFileName": "wwwroot/css/site.min.css", 6 | // An array of relative input file paths. Globbing patterns supported 7 | "inputFiles": [ 8 | "wwwroot/css/site.css" 9 | ] 10 | }, 11 | { 12 | "outputFileName": "wwwroot/js/site.min.js", 13 | "inputFiles": [ 14 | "wwwroot/js/site.js" 15 | ], 16 | // Optionally specify minification options 17 | "minify": { 18 | "enabled": true, 19 | "renameLocals": true 20 | }, 21 | // Optionally generate .map file 22 | "sourceMap": false 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | /* Carousel */ 14 | .carousel-caption p { 15 | font-size: 20px; 16 | line-height: 1.4; 17 | } 18 | 19 | /* Make .svg files in the carousel display properly in older browsers */ 20 | .carousel-inner .item img[src$=".svg"] { 21 | width: 100%; 22 | } 23 | 24 | /* QR code generator */ 25 | #qrCode { 26 | margin: 15px; 27 | } 28 | 29 | /* Hide/rearrange for smaller screens */ 30 | @media screen and (max-width: 767px) { 31 | /* Hide captions */ 32 | .carousel-caption { 33 | display: none; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter1/FooApi/Host/wwwroot/favicon.ico -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/images/banner1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/images/banner2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Write your JavaScript code. 2 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter1/FooApi/Host/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "version": "3.2.6", 4 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 5 | "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.", 6 | "main": [ 7 | "jquery.validate.unobtrusive.js" 8 | ], 9 | "ignore": [ 10 | "**/.*", 11 | "*.json", 12 | "*.md", 13 | "*.txt", 14 | "gulpfile.js" 15 | ], 16 | "keywords": [ 17 | "jquery", 18 | "asp.net", 19 | "mvc", 20 | "validation", 21 | "unobtrusive" 22 | ], 23 | "authors": [ 24 | "Microsoft" 25 | ], 26 | "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git" 30 | }, 31 | "dependencies": { 32 | "jquery-validation": ">=1.8", 33 | "jquery": ">=1.8" 34 | }, 35 | "_release": "3.2.6", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.2.6", 39 | "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7" 40 | }, 41 | "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git", 42 | "_target": "3.2.6", 43 | "_originalSource": "jquery-validation-unobtrusive" 44 | } -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Unobtrusive validation support library for jQuery and jQuery Validate 3 | ** Copyright (C) Microsoft Corporation. All rights reserved. 4 | */ 5 | !function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function m(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=p.unobtrusive.options||{},m=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),m("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),m("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),m("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var u,p=a.validator,v="unobtrusiveValidation";p.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=m(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){p.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=m(this);a&&a.attachValidation()})}},u=p.unobtrusive.adapters,u.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},u.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},u.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},u.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},p.addMethod("__dummy__",function(a,e,n){return!0}),p.addMethod("regex",function(a,e,n){var t;return this.optional(e)?!0:(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),p.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),p.methods.extension?(u.addSingleVal("accept","mimtype"),u.addSingleVal("extension","extension")):u.addSingleVal("extension","extension","accept"),u.addSingleVal("regex","pattern"),u.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),u.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),u.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),u.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),u.add("required",function(a){("INPUT"!==a.element.tagName.toUpperCase()||"CHECKBOX"!==a.element.type.toUpperCase())&&e(a,"required",!0)}),u.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),u.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),a(function(){p.unobtrusive.parse(document)})}(jQuery); -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "http://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jzaefferer/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.14.0", 31 | "_release": "1.14.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.14.0", 35 | "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48" 36 | }, 37 | "_source": "git://github.com/jzaefferer/jquery-validation.git", 38 | "_target": ">=1.8", 39 | "_originalSource": "jquery-validation" 40 | } -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "2.2.0", 16 | "_release": "2.2.0", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "2.2.0", 20 | "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5" 21 | }, 22 | "_source": "git://github.com/jquery/jquery-dist.git", 23 | "_target": "2.2.0", 24 | "_originalSource": "jquery" 25 | } -------------------------------------------------------------------------------- /samples/Chapter1/FooApi/Host/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/FooApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FooApi", "FooApi\FooApi.csproj", "{B6308A12-C556-4A53-9F94-AB43424D2EB7}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "Host\Host.csproj", "{1B7B0FF8-C0AE-4482-80E3-B9E190440E92}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionalTests", "FunctionalTests\FunctionalTests.csproj", "{4CF7F430-B5E9-46BA-8802-37D3AF425BF2}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {2C32CA1E-766F-4927-B9CB-DFC7ECE759D1} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/FooApi/Controllers/FooController.cs: -------------------------------------------------------------------------------- 1 | using FooApi.Models; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace FooApi.Controllers 5 | { 6 | [Route("api/v{version:apiVersion}/[controller]")] 7 | public class FooController : Controller 8 | { 9 | [HttpGet()] 10 | public IActionResult Get(int id) 11 | { 12 | var bar = new Bar() { Id = id }; 13 | 14 | return Ok(bar); 15 | } 16 | 17 | [HttpPost()] 18 | public IActionResult Post([FromBody]Bar bar) 19 | { 20 | return CreatedAtAction(nameof(Get), new { id = bar.Id }); 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/FooApi/FooApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/FooApi/FooConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace FooApi 9 | { 10 | public static class FooConfiguration 11 | { 12 | public static IServiceCollection ConfigureServices(IServiceCollection services) => 13 | services 14 | .AddApiVersioning(setup=> 15 | { 16 | setup.AssumeDefaultVersionWhenUnspecified = true; 17 | setup.DefaultApiVersion = new ApiVersion(1, 0); 18 | }) 19 | .AddMvc() 20 | .Services; 21 | 22 | public static void Configure(IApplicationBuilder app, Func configureHost) => 23 | configureHost(app) 24 | .UseMvc(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/FooApi/Models/Bar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FooApi.Models 6 | { 7 | public class Bar 8 | { 9 | public int Id { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/FunctionalTests/FunctionalTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/FunctionalTests/Scenarios/FooController.cs: -------------------------------------------------------------------------------- 1 | using FooApi.Controllers; 2 | using FooApi.Models; 3 | using FunctionalTests.Seedwork; 4 | using Microsoft.AspNetCore.TestHost; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | namespace FunctionalTests.Scenarios 9 | { 10 | [Collection("Foo")] 11 | public class foo_api_should 12 | { 13 | private readonly FooFixture Given; 14 | 15 | public foo_api_should(FooFixture fooFixture) 16 | { 17 | Given = fooFixture; 18 | } 19 | 20 | [Fact] 21 | public async Task get_bar_when_requested() 22 | { 23 | var response = await Given.FooServer 24 | .CreateHttpApiRequest(foo=>foo.Get(1),new { version = 1 }) 25 | .GetAsync(); 26 | 27 | response.EnsureSuccessStatusCode(); 28 | } 29 | 30 | [Fact] 31 | public async Task post_new_bar() 32 | { 33 | var bar = new Bar() { Id = 1 }; 34 | var response = await Given.FooServer 35 | .CreateHttpApiRequest(foo => foo.Post(bar), new { version = 1 }) 36 | .PostAsync(); 37 | 38 | response.EnsureSuccessStatusCode(); 39 | } 40 | } 41 | 42 | 43 | static class FooAPI 44 | { 45 | static string BASEURI = "api/v1/foo"; 46 | 47 | public static class Get 48 | { 49 | public static string Bar(int id) 50 | { 51 | return $"{BASEURI }?id={id}"; 52 | } 53 | } 54 | 55 | public static class Post 56 | { 57 | public static string Bar() 58 | { 59 | return BASEURI; 60 | } 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/FunctionalTests/Seedwork/FooFixture.cs: -------------------------------------------------------------------------------- 1 | using FooApi; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.TestHost; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | using Xunit; 10 | 11 | namespace FunctionalTests.Seedwork 12 | { 13 | public class FooFixture 14 | { 15 | public TestServer FooServer { get; private set; } 16 | 17 | public FooFixture() 18 | { 19 | var hostBuilder = new WebHostBuilder() 20 | .UseStartup(); 21 | 22 | FooServer = new TestServer(hostBuilder); 23 | } 24 | } 25 | 26 | [CollectionDefinition("Foo")] 27 | public class FooFixtureCollection 28 | : ICollectionFixture 29 | { 30 | } 31 | 32 | class TestStartup 33 | { 34 | public void ConfigureServices(IServiceCollection services) 35 | { 36 | FooConfiguration.ConfigureServices(services); 37 | } 38 | 39 | public void Configure(IApplicationBuilder app) 40 | { 41 | FooConfiguration.Configure(app, host => host); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/Host.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace Host 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | BuildWebHost(args).Run(); 18 | } 19 | 20 | public static IWebHost BuildWebHost(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup() 23 | .Build(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using FooApi; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | 11 | namespace Host 12 | { 13 | public class Startup 14 | { 15 | public Startup(IConfiguration configuration) 16 | { 17 | Configuration = configuration; 18 | } 19 | 20 | public IConfiguration Configuration { get; } 21 | 22 | // This method gets called by the runtime. Use this method to add services to the container. 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | FooConfiguration 26 | .ConfigureServices(services); 27 | } 28 | 29 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 30 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 31 | { 32 | FooConfiguration.Configure(app, host => 33 | { 34 | if (env.IsDevelopment()) 35 | { 36 | host.UseBrowserLink(); 37 | host.UseDeveloperExceptionPage(); 38 | } 39 | else 40 | { 41 | host.UseExceptionHandler("/Home/Error"); 42 | } 43 | 44 | host.UseStaticFiles(); 45 | 46 | return host; 47 | }); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/bundleconfig.json: -------------------------------------------------------------------------------- 1 | // Configure bundling and minification for the project. 2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241 3 | [ 4 | { 5 | "outputFileName": "wwwroot/css/site.min.css", 6 | // An array of relative input file paths. Globbing patterns supported 7 | "inputFiles": [ 8 | "wwwroot/css/site.css" 9 | ] 10 | }, 11 | { 12 | "outputFileName": "wwwroot/js/site.min.js", 13 | "inputFiles": [ 14 | "wwwroot/js/site.js" 15 | ], 16 | // Optionally specify minification options 17 | "minify": { 18 | "enabled": true, 19 | "renameLocals": true 20 | }, 21 | // Optionally generate .map file 22 | "sourceMap": false 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | /* Carousel */ 14 | .carousel-caption p { 15 | font-size: 20px; 16 | line-height: 1.4; 17 | } 18 | 19 | /* Make .svg files in the carousel display properly in older browsers */ 20 | .carousel-inner .item img[src$=".svg"] { 21 | width: 100%; 22 | } 23 | 24 | /* QR code generator */ 25 | #qrCode { 26 | margin: 15px; 27 | } 28 | 29 | /* Hide/rearrange for smaller screens */ 30 | @media screen and (max-width: 767px) { 31 | /* Hide captions */ 32 | .carousel-caption { 33 | display: none; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter2/FooApi/Host/wwwroot/favicon.ico -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/images/banner1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/images/banner2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Write your JavaScript code. 2 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter2/FooApi/Host/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "version": "3.2.6", 4 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 5 | "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.", 6 | "main": [ 7 | "jquery.validate.unobtrusive.js" 8 | ], 9 | "ignore": [ 10 | "**/.*", 11 | "*.json", 12 | "*.md", 13 | "*.txt", 14 | "gulpfile.js" 15 | ], 16 | "keywords": [ 17 | "jquery", 18 | "asp.net", 19 | "mvc", 20 | "validation", 21 | "unobtrusive" 22 | ], 23 | "authors": [ 24 | "Microsoft" 25 | ], 26 | "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git" 30 | }, 31 | "dependencies": { 32 | "jquery-validation": ">=1.8", 33 | "jquery": ">=1.8" 34 | }, 35 | "_release": "3.2.6", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.2.6", 39 | "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7" 40 | }, 41 | "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git", 42 | "_target": "3.2.6", 43 | "_originalSource": "jquery-validation-unobtrusive" 44 | } -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Unobtrusive validation support library for jQuery and jQuery Validate 3 | ** Copyright (C) Microsoft Corporation. All rights reserved. 4 | */ 5 | !function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function m(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=p.unobtrusive.options||{},m=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),m("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),m("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),m("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var u,p=a.validator,v="unobtrusiveValidation";p.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=m(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){p.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=m(this);a&&a.attachValidation()})}},u=p.unobtrusive.adapters,u.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},u.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},u.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},u.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},p.addMethod("__dummy__",function(a,e,n){return!0}),p.addMethod("regex",function(a,e,n){var t;return this.optional(e)?!0:(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),p.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),p.methods.extension?(u.addSingleVal("accept","mimtype"),u.addSingleVal("extension","extension")):u.addSingleVal("extension","extension","accept"),u.addSingleVal("regex","pattern"),u.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),u.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),u.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),u.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),u.add("required",function(a){("INPUT"!==a.element.tagName.toUpperCase()||"CHECKBOX"!==a.element.type.toUpperCase())&&e(a,"required",!0)}),u.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),u.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),a(function(){p.unobtrusive.parse(document)})}(jQuery); -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "http://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jzaefferer/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.14.0", 31 | "_release": "1.14.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.14.0", 35 | "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48" 36 | }, 37 | "_source": "git://github.com/jzaefferer/jquery-validation.git", 38 | "_target": ">=1.8", 39 | "_originalSource": "jquery-validation" 40 | } -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "2.2.0", 16 | "_release": "2.2.0", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "2.2.0", 20 | "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5" 21 | }, 22 | "_source": "git://github.com/jquery/jquery-dist.git", 23 | "_target": "2.2.0", 24 | "_originalSource": "jquery" 25 | } -------------------------------------------------------------------------------- /samples/Chapter2/FooApi/Host/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/FooApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FooApi", "FooApi\FooApi.csproj", "{B6308A12-C556-4A53-9F94-AB43424D2EB7}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "Host\Host.csproj", "{1B7B0FF8-C0AE-4482-80E3-B9E190440E92}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionalTests", "FunctionalTests\FunctionalTests.csproj", "{4CF7F430-B5E9-46BA-8802-37D3AF425BF2}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {2C32CA1E-766F-4927-B9CB-DFC7ECE759D1} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/FooApi/Controllers/FooController.cs: -------------------------------------------------------------------------------- 1 | using FooApi.Models; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace FooApi.Controllers 6 | { 7 | [Route("api/v{version:apiVersion}/[controller]")] 8 | public class FooController : Controller 9 | { 10 | [HttpGet()] 11 | [Authorize("GetPolicy")] 12 | public IActionResult Get(int id) 13 | { 14 | var bar = new Bar() { Id = id }; 15 | 16 | return Ok(bar); 17 | } 18 | 19 | [HttpPost()] 20 | [Authorize("PostPolicy")] 21 | public IActionResult Post([FromBody]Bar bar) 22 | { 23 | return CreatedAtAction(nameof(Get), new { id = bar.Id }); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/FooApi/FooApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/FooApi/FooConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace FooApi 9 | { 10 | public static class FooConfiguration 11 | { 12 | public static IServiceCollection ConfigureServices(IServiceCollection services) => 13 | services 14 | .AddApiVersioning(setup=> 15 | { 16 | setup.AssumeDefaultVersionWhenUnspecified = true; 17 | setup.DefaultApiVersion = new ApiVersion(1, 0); 18 | }) 19 | .AddAuthorization(setup=> 20 | { 21 | setup.AddPolicy("GetPolicy", requirements => 22 | { 23 | requirements.RequireClaim("Permission", new string[] { "Read" }); 24 | }); 25 | 26 | setup.AddPolicy("PostPolicy", requirements => 27 | { 28 | requirements.RequireClaim("Permission", new string[] { "Write" }); 29 | }); 30 | }) 31 | .AddMvc() 32 | .Services; 33 | 34 | public static void Configure(IApplicationBuilder app, Func configureHost) => 35 | configureHost(app) 36 | .UseMvc(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/FooApi/Models/Bar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FooApi.Models 6 | { 7 | public class Bar 8 | { 9 | public int Id { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/FunctionalTests/FunctionalTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/FunctionalTests/Scenarios/FooController.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using FooApi.Controllers; 3 | using FooApi.Models; 4 | using FunctionalTests.Seedwork; 5 | using Microsoft.AspNetCore.TestHost; 6 | using System.Collections.Generic; 7 | using System.Net; 8 | using System.Security.Claims; 9 | using System.Threading.Tasks; 10 | using Xunit; 11 | 12 | namespace FunctionalTests.Scenarios 13 | { 14 | [Collection("Foo")] 15 | public class foo_api_should 16 | { 17 | private readonly FooFixture Given; 18 | 19 | public foo_api_should(FooFixture fooFixture) 20 | { 21 | Given = fooFixture; 22 | } 23 | 24 | [Fact] 25 | public async Task get_bar_when_requested() 26 | { 27 | var response = await Given.FooServer 28 | .CreateHttpApiRequest(foo=>foo.Get(1),new { version = 1 }) 29 | .WithIdentity(new List() { new Claim("Permission","Read")}) 30 | .GetAsync(); 31 | 32 | response.EnsureSuccessStatusCode(); 33 | } 34 | 35 | [Fact] 36 | public async Task get_forbideen_if_not_authenticated_when_requested() 37 | { 38 | var response = await Given.FooServer 39 | .CreateHttpApiRequest(foo => foo.Get(1), new { version = 1 }) 40 | .WithIdentity(new List() { new Claim("Permission", "NonReadClaim") }) 41 | .GetAsync(); 42 | 43 | response.StatusCode 44 | .Should() 45 | .Be(HttpStatusCode.Forbidden); 46 | } 47 | 48 | [Fact] 49 | public async Task post_new_bar() 50 | { 51 | var bar = new Bar() { Id = 1 }; 52 | var response = await Given.FooServer 53 | .CreateHttpApiRequest(foo => foo.Post(bar), new { version = 1 }) 54 | .WithIdentity(new List() { new Claim("Permission", "Write") }) 55 | .PostAsync(); 56 | 57 | response.EnsureSuccessStatusCode(); 58 | } 59 | 60 | [Fact] 61 | public async Task post_get_forbidden_if_not_authenticated_when_requested() 62 | { 63 | var bar = new Bar() { Id = 1 }; 64 | var response = await Given.FooServer 65 | .CreateHttpApiRequest(foo => foo.Post(bar), new { version = 1 }) 66 | .WithIdentity(new List() { new Claim("Permission", "NonWriteClaim") }) 67 | .PostAsync(); 68 | 69 | response.StatusCode 70 | .Should() 71 | .Be(HttpStatusCode.Forbidden); 72 | } 73 | } 74 | 75 | 76 | static class FooAPI 77 | { 78 | static string BASEURI = "api/v1/foo"; 79 | 80 | public static class Get 81 | { 82 | public static string Bar(int id) 83 | { 84 | return $"{BASEURI }?id={id}"; 85 | } 86 | } 87 | 88 | public static class Post 89 | { 90 | public static string Bar() 91 | { 92 | return BASEURI; 93 | } 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/FunctionalTests/Seedwork/FooFixture.cs: -------------------------------------------------------------------------------- 1 | using Acheve.AspNetCore.TestHost.Security; 2 | using FooApi; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.TestHost; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Xunit; 11 | 12 | namespace FunctionalTests.Seedwork 13 | { 14 | public class FooFixture 15 | { 16 | public TestServer FooServer { get; private set; } 17 | 18 | public FooFixture() 19 | { 20 | var hostBuilder = new WebHostBuilder() 21 | .UseStartup(); 22 | 23 | FooServer = new TestServer(hostBuilder); 24 | } 25 | } 26 | 27 | [CollectionDefinition("Foo")] 28 | public class FooFixtureCollection 29 | : ICollectionFixture 30 | { 31 | } 32 | 33 | class TestStartup 34 | { 35 | public void ConfigureServices(IServiceCollection services) 36 | { 37 | FooConfiguration.ConfigureServices(services) 38 | .AddAuthentication(defaultScheme: "TestServer") 39 | .AddTestServerAuthentication(); 40 | } 41 | 42 | public void Configure(IApplicationBuilder app) 43 | { 44 | FooConfiguration.Configure(app, host => host.UseAuthentication()); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/FunctionalTests/Seedwork/MyTestsAuthenticationHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Security.Claims; 7 | using System.Text; 8 | using System.Text.Encodings.Web; 9 | using System.Threading.Tasks; 10 | 11 | namespace FunctionalTests.Seedwork 12 | { 13 | public class MyTestsAuthenticationHandler 14 | : AuthenticationHandler 15 | { 16 | public MyTestsAuthenticationHandler(IOptionsMonitor options, 17 | ILoggerFactory logger, 18 | UrlEncoder encoder, 19 | ISystemClock clock) 20 | : base(options, logger, encoder, clock) 21 | { 22 | } 23 | 24 | protected override Task HandleAuthenticateAsync() 25 | { 26 | var claims = new List() 27 | { 28 | new Claim(ClaimTypes.Name,"HttpAPITesting"), 29 | new Claim("Permission","Read") 30 | }; 31 | 32 | var identity = new ClaimsIdentity( 33 | claims: claims, 34 | authenticationType: Scheme.Name, 35 | nameType: ClaimTypes.Name, 36 | roleType: ClaimTypes.Role); 37 | 38 | var ticket = new AuthenticationTicket( 39 | new ClaimsPrincipal(identity), 40 | new AuthenticationProperties(), 41 | Scheme.Name); 42 | 43 | return Task.FromResult(AuthenticateResult.Success(ticket)); 44 | } 45 | } 46 | 47 | public class MyTestOptions 48 | : AuthenticationSchemeOptions 49 | { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/Host.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace Host 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | BuildWebHost(args).Run(); 18 | } 19 | 20 | public static IWebHost BuildWebHost(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup() 23 | .Build(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using FooApi; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | 11 | namespace Host 12 | { 13 | public class Startup 14 | { 15 | public Startup(IConfiguration configuration) 16 | { 17 | Configuration = configuration; 18 | } 19 | 20 | public IConfiguration Configuration { get; } 21 | 22 | // This method gets called by the runtime. Use this method to add services to the container. 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | FooConfiguration 26 | .ConfigureServices(services) 27 | .AddAuthentication() 28 | .AddJwtBearer(); 29 | } 30 | 31 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 32 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 33 | { 34 | FooConfiguration.Configure(app, host => 35 | host.UseStaticFiles() 36 | .UseAuthentication() 37 | .UseExceptionHandler("/Home/Error")); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/bundleconfig.json: -------------------------------------------------------------------------------- 1 | // Configure bundling and minification for the project. 2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241 3 | [ 4 | { 5 | "outputFileName": "wwwroot/css/site.min.css", 6 | // An array of relative input file paths. Globbing patterns supported 7 | "inputFiles": [ 8 | "wwwroot/css/site.css" 9 | ] 10 | }, 11 | { 12 | "outputFileName": "wwwroot/js/site.min.js", 13 | "inputFiles": [ 14 | "wwwroot/js/site.js" 15 | ], 16 | // Optionally specify minification options 17 | "minify": { 18 | "enabled": true, 19 | "renameLocals": true 20 | }, 21 | // Optionally generate .map file 22 | "sourceMap": false 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | /* Carousel */ 14 | .carousel-caption p { 15 | font-size: 20px; 16 | line-height: 1.4; 17 | } 18 | 19 | /* Make .svg files in the carousel display properly in older browsers */ 20 | .carousel-inner .item img[src$=".svg"] { 21 | width: 100%; 22 | } 23 | 24 | /* QR code generator */ 25 | #qrCode { 26 | margin: 15px; 27 | } 28 | 29 | /* Hide/rearrange for smaller screens */ 30 | @media screen and (max-width: 767px) { 31 | /* Hide captions */ 32 | .carousel-caption { 33 | display: none; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter3/FooApi/Host/wwwroot/favicon.ico -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/images/banner1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/images/banner2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Write your JavaScript code. 2 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter3/FooApi/Host/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "version": "3.2.6", 4 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 5 | "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.", 6 | "main": [ 7 | "jquery.validate.unobtrusive.js" 8 | ], 9 | "ignore": [ 10 | "**/.*", 11 | "*.json", 12 | "*.md", 13 | "*.txt", 14 | "gulpfile.js" 15 | ], 16 | "keywords": [ 17 | "jquery", 18 | "asp.net", 19 | "mvc", 20 | "validation", 21 | "unobtrusive" 22 | ], 23 | "authors": [ 24 | "Microsoft" 25 | ], 26 | "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git" 30 | }, 31 | "dependencies": { 32 | "jquery-validation": ">=1.8", 33 | "jquery": ">=1.8" 34 | }, 35 | "_release": "3.2.6", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.2.6", 39 | "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7" 40 | }, 41 | "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git", 42 | "_target": "3.2.6", 43 | "_originalSource": "jquery-validation-unobtrusive" 44 | } -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Unobtrusive validation support library for jQuery and jQuery Validate 3 | ** Copyright (C) Microsoft Corporation. All rights reserved. 4 | */ 5 | !function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function m(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=p.unobtrusive.options||{},m=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),m("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),m("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),m("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var u,p=a.validator,v="unobtrusiveValidation";p.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=m(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){p.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=m(this);a&&a.attachValidation()})}},u=p.unobtrusive.adapters,u.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},u.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},u.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},u.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},p.addMethod("__dummy__",function(a,e,n){return!0}),p.addMethod("regex",function(a,e,n){var t;return this.optional(e)?!0:(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),p.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),p.methods.extension?(u.addSingleVal("accept","mimtype"),u.addSingleVal("extension","extension")):u.addSingleVal("extension","extension","accept"),u.addSingleVal("regex","pattern"),u.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),u.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),u.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),u.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),u.add("required",function(a){("INPUT"!==a.element.tagName.toUpperCase()||"CHECKBOX"!==a.element.type.toUpperCase())&&e(a,"required",!0)}),u.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),u.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),a(function(){p.unobtrusive.parse(document)})}(jQuery); -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "http://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jzaefferer/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.14.0", 31 | "_release": "1.14.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.14.0", 35 | "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48" 36 | }, 37 | "_source": "git://github.com/jzaefferer/jquery-validation.git", 38 | "_target": ">=1.8", 39 | "_originalSource": "jquery-validation" 40 | } -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "2.2.0", 16 | "_release": "2.2.0", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "2.2.0", 20 | "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5" 21 | }, 22 | "_source": "git://github.com/jquery/jquery-dist.git", 23 | "_target": "2.2.0", 24 | "_originalSource": "jquery" 25 | } -------------------------------------------------------------------------------- /samples/Chapter3/FooApi/Host/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FooApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FooApi", "FooApi\FooApi.csproj", "{B6308A12-C556-4A53-9F94-AB43424D2EB7}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "Host\Host.csproj", "{1B7B0FF8-C0AE-4482-80E3-B9E190440E92}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionalTests", "FunctionalTests\FunctionalTests.csproj", "{4CF7F430-B5E9-46BA-8802-37D3AF425BF2}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {B6308A12-C556-4A53-9F94-AB43424D2EB7}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {1B7B0FF8-C0AE-4482-80E3-B9E190440E92}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {4CF7F430-B5E9-46BA-8802-37D3AF425BF2}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {2C32CA1E-766F-4927-B9CB-DFC7ECE759D1} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FooApi/Controllers/FooController.cs: -------------------------------------------------------------------------------- 1 | using FooApi.Models; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Threading.Tasks; 5 | 6 | namespace FooApi.Controllers 7 | { 8 | [Route("api/v{version:apiVersion}/[controller]")] 9 | public class FooController : Controller 10 | { 11 | private readonly FooDbContext context; 12 | 13 | public FooController(FooDbContext context) 14 | { 15 | this.context = context; 16 | } 17 | 18 | [HttpGet()] 19 | [Authorize("GetPolicy")] 20 | public async Task Get(int id) 21 | { 22 | var bar = await context.Bars.FindAsync(id); 23 | 24 | return Ok(bar); 25 | } 26 | 27 | [HttpPost()] 28 | [Authorize("PostPolicy")] 29 | public async Task Post([FromBody]Bar bar) 30 | { 31 | await context.AddAsync(bar); 32 | return CreatedAtAction(nameof(Get), new { id = bar.Id }); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FooApi/FooApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FooApi/FooConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace FooApi 9 | { 10 | public static class FooConfiguration 11 | { 12 | public static IServiceCollection ConfigureServices(IServiceCollection services) => 13 | services 14 | .AddApiVersioning(setup=> 15 | { 16 | setup.AssumeDefaultVersionWhenUnspecified = true; 17 | setup.DefaultApiVersion = new ApiVersion(1, 0); 18 | }) 19 | .AddAuthorization(setup=> 20 | { 21 | setup.AddPolicy("GetPolicy", requirements => 22 | { 23 | requirements.RequireClaim("Permission", new string[] { "Read" }); 24 | }); 25 | 26 | setup.AddPolicy("PostPolicy", requirements => 27 | { 28 | requirements.RequireClaim("Permission", new string[] { "Write" }); 29 | }); 30 | }) 31 | .AddMvc() 32 | .Services; 33 | 34 | public static void Configure(IApplicationBuilder app, Func configureHost) => 35 | configureHost(app) 36 | .UseMvc(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FooApi/IWebHostExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace Microsoft.AspNetCore.Hosting 9 | { 10 | public static class IWebHostExtensions 11 | { 12 | public static IWebHost MigrateDbContext(this IWebHost webHost, Action seeder) where TContext : DbContext 13 | { 14 | using (var scope = webHost.Services.CreateScope()) 15 | { 16 | var services = scope.ServiceProvider; 17 | 18 | var logger = services.GetRequiredService>(); 19 | 20 | var context = services.GetService(); 21 | 22 | try 23 | { 24 | logger.LogInformation($"Migrating database associated with context {typeof(TContext).Name}"); 25 | 26 | context.Database 27 | .Migrate(); 28 | 29 | seeder(context, services); 30 | 31 | logger.LogInformation($"Migrated database associated with context {typeof(TContext).Name}"); 32 | } 33 | catch (Exception ex) 34 | { 35 | logger.LogError(ex, $"An error occurred while migrating the database used on context {typeof(TContext).Name}"); 36 | } 37 | } 38 | 39 | return webHost; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FooApi/Migrations/20180307192205_Initial.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using FooApi.Models; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage; 8 | using System; 9 | 10 | namespace FooApi.Migrations 11 | { 12 | [DbContext(typeof(FooDbContext))] 13 | [Migration("20180307192205_Initial")] 14 | partial class Initial 15 | { 16 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .HasAnnotation("ProductVersion", "2.0.1-rtm-125") 21 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 22 | 23 | modelBuilder.Entity("FooApi.Models.Bar", b => 24 | { 25 | b.Property("Id") 26 | .ValueGeneratedOnAdd(); 27 | 28 | b.HasKey("Id"); 29 | 30 | b.ToTable("Bars"); 31 | }); 32 | #pragma warning restore 612, 618 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FooApi/Migrations/20180307192205_Initial.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace FooApi.Migrations 7 | { 8 | public partial class Initial : Migration 9 | { 10 | protected override void Up(MigrationBuilder migrationBuilder) 11 | { 12 | migrationBuilder.CreateTable( 13 | name: "Bars", 14 | columns: table => new 15 | { 16 | Id = table.Column(nullable: false) 17 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn) 18 | }, 19 | constraints: table => 20 | { 21 | table.PrimaryKey("PK_Bars", x => x.Id); 22 | }); 23 | } 24 | 25 | protected override void Down(MigrationBuilder migrationBuilder) 26 | { 27 | migrationBuilder.DropTable( 28 | name: "Bars"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FooApi/Migrations/FooDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using FooApi.Models; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage; 8 | using System; 9 | 10 | namespace FooApi.Migrations 11 | { 12 | [DbContext(typeof(FooDbContext))] 13 | partial class FooDbContextModelSnapshot : ModelSnapshot 14 | { 15 | protected override void BuildModel(ModelBuilder modelBuilder) 16 | { 17 | #pragma warning disable 612, 618 18 | modelBuilder 19 | .HasAnnotation("ProductVersion", "2.0.1-rtm-125") 20 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 21 | 22 | modelBuilder.Entity("FooApi.Models.Bar", b => 23 | { 24 | b.Property("Id") 25 | .ValueGeneratedOnAdd(); 26 | 27 | b.HasKey("Id"); 28 | 29 | b.ToTable("Bars"); 30 | }); 31 | #pragma warning restore 612, 618 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FooApi/Models/Bar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FooApi.Models 6 | { 7 | public class Bar 8 | { 9 | public int Id { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FooApi/Models/FooDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace FooApi.Models 7 | { 8 | public class FooDbContext : DbContext 9 | { 10 | public DbSet Bars { get; set; } 11 | 12 | public FooDbContext(DbContextOptions options) : base(options) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FunctionalTests/FunctionalTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FunctionalTests/Scenarios/FooController.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using FooApi.Controllers; 3 | using FooApi.Models; 4 | using FunctionalTests.Seedwork; 5 | using Microsoft.AspNetCore.TestHost; 6 | using Newtonsoft.Json; 7 | using System.Collections.Generic; 8 | using System.Net; 9 | using System.Security.Claims; 10 | using System.Threading.Tasks; 11 | using Xunit; 12 | 13 | namespace FunctionalTests.Scenarios 14 | { 15 | [Collection("Foo")] 16 | public class foo_api_should 17 | { 18 | private readonly FooFixture Given; 19 | 20 | public foo_api_should(FooFixture fooFixture) 21 | { 22 | Given = fooFixture; 23 | } 24 | 25 | [Fact] 26 | [ResetDatabase] 27 | public async Task get_bar_when_requested() 28 | { 29 | var bar = await Given.ABarInTheDatabase(); 30 | var response = await Given.FooServer 31 | .CreateHttpApiRequest(foo=>foo.Get(bar.Id),new { version = 1 }) 32 | .WithIdentity(new List() { new Claim("Permission","Read")}) 33 | .GetAsync(); 34 | 35 | response.EnsureSuccessStatusCode(); 36 | var json = await response.Content.ReadAsStringAsync(); 37 | var result = JsonConvert.DeserializeObject(json); 38 | result.Id.Should().Be(bar.Id); 39 | } 40 | 41 | [Fact] 42 | public async Task get_forbideen_if_not_authenticated_when_requested() 43 | { 44 | var response = await Given.FooServer 45 | .CreateHttpApiRequest(foo => foo.Get(1), new { version = 1 }) 46 | .WithIdentity(new List() { new Claim("Permission", "NonReadClaim") }) 47 | .GetAsync(); 48 | 49 | response.StatusCode 50 | .Should() 51 | .Be(HttpStatusCode.Forbidden); 52 | } 53 | 54 | [Fact] 55 | public async Task post_new_bar() 56 | { 57 | var bar = new Bar() { Id = 1 }; 58 | var response = await Given.FooServer 59 | .CreateHttpApiRequest(foo => foo.Post(bar), new { version = 1 }) 60 | .WithIdentity(new List() { new Claim("Permission", "Write") }) 61 | .PostAsync(); 62 | 63 | response.EnsureSuccessStatusCode(); 64 | } 65 | 66 | [Fact] 67 | public async Task post_get_forbidden_if_not_authenticated_when_requested() 68 | { 69 | var bar = new Bar() { Id = 1 }; 70 | var response = await Given.FooServer 71 | .CreateHttpApiRequest(foo => foo.Post(bar), new { version = 1 }) 72 | .WithIdentity(new List() { new Claim("Permission", "NonWriteClaim") }) 73 | .PostAsync(); 74 | 75 | response.StatusCode 76 | .Should() 77 | .Be(HttpStatusCode.Forbidden); 78 | } 79 | } 80 | 81 | 82 | static class FooAPI 83 | { 84 | static string BASEURI = "api/v1/foo"; 85 | 86 | public static class Get 87 | { 88 | public static string Bar(int id) 89 | { 90 | return $"{BASEURI }?id={id}"; 91 | } 92 | } 93 | 94 | public static class Post 95 | { 96 | public static string Bar() 97 | { 98 | return BASEURI; 99 | } 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FunctionalTests/Seedwork/FooFixture.cs: -------------------------------------------------------------------------------- 1 | using Acheve.AspNetCore.TestHost.Security; 2 | using FooApi; 3 | using FooApi.Models; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.AspNetCore.TestHost; 7 | using Microsoft.EntityFrameworkCore; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Respawn; 10 | using System; 11 | using System.Threading.Tasks; 12 | using Xunit; 13 | 14 | namespace FunctionalTests.Seedwork 15 | { 16 | public class FooFixture 17 | { 18 | private static Checkpoint CheckPoint = new Checkpoint(); 19 | public TestServer FooServer { get; private set; } 20 | 21 | public FooFixture() 22 | { 23 | var hostBuilder = new WebHostBuilder() 24 | .UseStartup(); 25 | 26 | FooServer = new TestServer(hostBuilder); 27 | 28 | FooServer.Host.MigrateDbContext((ctx,sp)=> { }); 29 | 30 | CheckPoint.TablesToIgnore = new[] { "__EFMigrationsHistory" }; 31 | } 32 | 33 | public async Task ExecuteScopeAsync(Func action) 34 | { 35 | using (var scope = FooServer.Host.Services.GetService().CreateScope()) 36 | { 37 | await action(scope.ServiceProvider); 38 | } 39 | } 40 | 41 | public async Task ExecuteDbContextAsync(Func action) 42 | { 43 | await ExecuteScopeAsync(sp => action(sp.GetService())); 44 | } 45 | 46 | public static void ResetDatabase() 47 | { 48 | CheckPoint.Reset(@"Server=LRUIZ-LAPTOP\SQLEXPRESS;Database=Foo;Integrated Security=true").Wait(); 49 | } 50 | 51 | public async Task ABarInTheDatabase() 52 | { 53 | var bar = new Bar(); 54 | await ExecuteDbContextAsync(async context => 55 | { 56 | await context.AddAsync(bar); 57 | await context.SaveChangesAsync(); 58 | }); 59 | return bar; 60 | } 61 | } 62 | 63 | [CollectionDefinition("Foo")] 64 | public class FooFixtureCollection 65 | : ICollectionFixture 66 | { 67 | } 68 | 69 | class TestStartup 70 | { 71 | public void ConfigureServices(IServiceCollection services) 72 | { 73 | FooConfiguration.ConfigureServices(services) 74 | .AddDbContext(options => 75 | { 76 | options.UseSqlServer(@"Server=LRUIZ-LAPTOP\SQLEXPRESS;Database=Foo;Integrated Security=true", sqlOptions => 77 | { 78 | sqlOptions.MigrationsAssembly(typeof(Bar).Assembly.GetName().Name); 79 | }); 80 | }) 81 | .AddAuthentication(defaultScheme: "TestServer") 82 | .AddTestServerAuthentication(); 83 | } 84 | 85 | public void Configure(IApplicationBuilder app) 86 | { 87 | FooConfiguration.Configure(app, host => host.UseAuthentication()); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FunctionalTests/Seedwork/MyTestsAuthenticationHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Security.Claims; 7 | using System.Text; 8 | using System.Text.Encodings.Web; 9 | using System.Threading.Tasks; 10 | 11 | namespace FunctionalTests.Seedwork 12 | { 13 | public class MyTestsAuthenticationHandler 14 | : AuthenticationHandler 15 | { 16 | public MyTestsAuthenticationHandler(IOptionsMonitor options, 17 | ILoggerFactory logger, 18 | UrlEncoder encoder, 19 | ISystemClock clock) 20 | : base(options, logger, encoder, clock) 21 | { 22 | } 23 | 24 | protected override Task HandleAuthenticateAsync() 25 | { 26 | var claims = new List() 27 | { 28 | new Claim(ClaimTypes.Name,"HttpAPITesting"), 29 | new Claim("Permission","Read") 30 | }; 31 | 32 | var identity = new ClaimsIdentity( 33 | claims: claims, 34 | authenticationType: Scheme.Name, 35 | nameType: ClaimTypes.Name, 36 | roleType: ClaimTypes.Role); 37 | 38 | var ticket = new AuthenticationTicket( 39 | new ClaimsPrincipal(identity), 40 | new AuthenticationProperties(), 41 | Scheme.Name); 42 | 43 | return Task.FromResult(AuthenticateResult.Success(ticket)); 44 | } 45 | } 46 | 47 | public class MyTestOptions 48 | : AuthenticationSchemeOptions 49 | { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/FunctionalTests/Seedwork/ResetDatabaseAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Xunit.Sdk; 3 | 4 | namespace FunctionalTests.Seedwork 5 | { 6 | public class ResetDatabaseAttribute : BeforeAfterTestAttribute 7 | { 8 | public override void Before(MethodInfo methodUnderTest) 9 | { 10 | FooFixture.ResetDatabase(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/Host.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using FooApi.Models; 7 | using Microsoft.AspNetCore; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace Host 13 | { 14 | public class Program 15 | { 16 | public static void Main(string[] args) 17 | { 18 | BuildWebHost(args) 19 | .MigrateDbContext((ctx,sp)=> { }) 20 | .Run(); 21 | } 22 | 23 | public static IWebHost BuildWebHost(string[] args) => 24 | WebHost.CreateDefaultBuilder(args) 25 | .UseStartup() 26 | .Build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using FooApi; 6 | using FooApi.Models; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.EntityFrameworkCore; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | 13 | namespace Host 14 | { 15 | public class Startup 16 | { 17 | public Startup(IConfiguration configuration) 18 | { 19 | Configuration = configuration; 20 | } 21 | 22 | public IConfiguration Configuration { get; } 23 | 24 | // This method gets called by the runtime. Use this method to add services to the container. 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | FooConfiguration 28 | .ConfigureServices(services) 29 | .AddDbContext(options => 30 | { 31 | options.UseSqlServer(@"Server=LRUIZ-LAPTOP\SQLEXPRESS;Database=Foo;Integrated Security=true", sqlOptions => 32 | { 33 | sqlOptions.MigrationsAssembly(typeof(Bar).Assembly.GetName().Name); 34 | }); 35 | }) 36 | .AddAuthentication() 37 | .AddJwtBearer(); 38 | } 39 | 40 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 41 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 42 | { 43 | FooConfiguration.Configure(app, host => 44 | host.UseStaticFiles() 45 | .UseAuthentication() 46 | .UseExceptionHandler("/Home/Error")); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/bundleconfig.json: -------------------------------------------------------------------------------- 1 | // Configure bundling and minification for the project. 2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241 3 | [ 4 | { 5 | "outputFileName": "wwwroot/css/site.min.css", 6 | // An array of relative input file paths. Globbing patterns supported 7 | "inputFiles": [ 8 | "wwwroot/css/site.css" 9 | ] 10 | }, 11 | { 12 | "outputFileName": "wwwroot/js/site.min.js", 13 | "inputFiles": [ 14 | "wwwroot/js/site.js" 15 | ], 16 | // Optionally specify minification options 17 | "minify": { 18 | "enabled": true, 19 | "renameLocals": true 20 | }, 21 | // Optionally generate .map file 22 | "sourceMap": false 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | /* Carousel */ 14 | .carousel-caption p { 15 | font-size: 20px; 16 | line-height: 1.4; 17 | } 18 | 19 | /* Make .svg files in the carousel display properly in older browsers */ 20 | .carousel-inner .item img[src$=".svg"] { 21 | width: 100%; 22 | } 23 | 24 | /* QR code generator */ 25 | #qrCode { 26 | margin: 15px; 27 | } 28 | 29 | /* Hide/rearrange for smaller screens */ 30 | @media screen and (max-width: 767px) { 31 | /* Hide captions */ 32 | .carousel-caption { 33 | display: none; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter4/FooApi/Host/wwwroot/favicon.ico -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/images/banner2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Write your JavaScript code. 2 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter4/FooApi/Host/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xabaril/ManualEffectiveTestingHttpAPI/4f8c8611d30fd264e28f7b406d178fe4b4623c9b/samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "version": "3.2.6", 4 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 5 | "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.", 6 | "main": [ 7 | "jquery.validate.unobtrusive.js" 8 | ], 9 | "ignore": [ 10 | "**/.*", 11 | "*.json", 12 | "*.md", 13 | "*.txt", 14 | "gulpfile.js" 15 | ], 16 | "keywords": [ 17 | "jquery", 18 | "asp.net", 19 | "mvc", 20 | "validation", 21 | "unobtrusive" 22 | ], 23 | "authors": [ 24 | "Microsoft" 25 | ], 26 | "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git" 30 | }, 31 | "dependencies": { 32 | "jquery-validation": ">=1.8", 33 | "jquery": ">=1.8" 34 | }, 35 | "_release": "3.2.6", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.2.6", 39 | "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7" 40 | }, 41 | "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git", 42 | "_target": "3.2.6", 43 | "_originalSource": "jquery-validation-unobtrusive" 44 | } -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Unobtrusive validation support library for jQuery and jQuery Validate 3 | ** Copyright (C) Microsoft Corporation. All rights reserved. 4 | */ 5 | !function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function m(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=p.unobtrusive.options||{},m=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),m("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),m("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),m("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var u,p=a.validator,v="unobtrusiveValidation";p.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=m(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){p.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=m(this);a&&a.attachValidation()})}},u=p.unobtrusive.adapters,u.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},u.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},u.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},u.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},p.addMethod("__dummy__",function(a,e,n){return!0}),p.addMethod("regex",function(a,e,n){var t;return this.optional(e)?!0:(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),p.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),p.methods.extension?(u.addSingleVal("accept","mimtype"),u.addSingleVal("extension","extension")):u.addSingleVal("extension","extension","accept"),u.addSingleVal("regex","pattern"),u.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),u.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),u.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),u.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),u.add("required",function(a){("INPUT"!==a.element.tagName.toUpperCase()||"CHECKBOX"!==a.element.type.toUpperCase())&&e(a,"required",!0)}),u.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),u.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),a(function(){p.unobtrusive.parse(document)})}(jQuery); -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "http://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jzaefferer/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.14.0", 31 | "_release": "1.14.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.14.0", 35 | "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48" 36 | }, 37 | "_source": "git://github.com/jzaefferer/jquery-validation.git", 38 | "_target": ">=1.8", 39 | "_originalSource": "jquery-validation" 40 | } -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "2.2.0", 16 | "_release": "2.2.0", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "2.2.0", 20 | "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5" 21 | }, 22 | "_source": "git://github.com/jquery/jquery-dist.git", 23 | "_target": "2.2.0", 24 | "_originalSource": "jquery" 25 | } -------------------------------------------------------------------------------- /samples/Chapter4/FooApi/Host/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | --------------------------------------------------------------------------------