public void EnsureProviderApiHonoursPactWithConsumer() { // Arrange var config = new PactVerifierConfig { // NOTE: We default to using a ConsoleOutput, // however xUnit 2 does not capture the console output, // so a custom outputter is required. Outputters = new List <IOutput> { new XUnitOutput(_outputHelper) }, // Output verbose verification logs to the test output Verbose = true }; //Act / Assert IPactVerifier pactVerifier = new PactVerifier(config); pactVerifier.ProviderState($"{_pactServiceUri}/") .ServiceProvider("Provider", _providerUri) .HonoursPactWith("Consumer") .PactUri(@"..\..\..\..\..\pacts\consumer-provider.json") .Verify(description: "A invalid GET request for Date Validation with empty string date parameter" , providerState: String.Empty); }
public async Task VerificationForMessagePactShouldFailWhenWrongMessageIsReturned() { var recipeRepository = new FakeRecipeRepository(); var providerStateHandler = new ProviderStateHandler(recipeRepository); var config = new PactVerifierConfig { ProviderStateHandler = providerStateHandler.Handle, MessageProducer = (d) => null }; var pactVerifier = new PactVerifier(config); var buildDirectory = AppContext.BaseDirectory; var pactDir = Path.GetFullPath($"{buildDirectory}{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}pacts{Path.DirectorySeparatorChar}"); try { await pactVerifier.VerifyPactAsync(pactDir + "messageConsumer-messageProvider.json"); } catch (PactVerificationException e) { Assert.IsTrue(e.Message.Contains("Expected body or contents to be present, but was not")); throw; } }
public void EnsurePassengersApiHonoursPactWithFlightsApi() { //Arrange var config = new PactVerifierConfig { Outputters = new List <IOutput> { new XUnitOutput(output) }, Verbose = true, ProviderVersion = "0.0.1" }; const string providerUrl = "http://localhost:5001"; WebHost.CreateDefaultBuilder(new string[0]) .UseStartup <TestStartup>() .UseUrls(providerUrl) .Build().Start(); IPactVerifier pactVerifier = new PactVerifier(config); pactVerifier .ProviderState($"{providerUrl}/provider-states") .ServiceProvider("PassengersApi", providerUrl) .HonoursPactWith("FlightsApi") .PactUri("..\\..\\..\\..\\..\\PactExample.FlightsApi\\FlightsApiTests\\pacts\\flightsapi-passengersapi.json") .Verify(); }
public void EnsureProviderApiHonoursPactWithConsumer() { // Arrange var config = new PactVerifierConfig { // NOTE: We default to using a ConsoleOutput, // however xUnit 2 does not capture the console output, // so a custom outputter is required. Outputters = new List <IOutput> { new XUnitOutput(_outputHelper) }, // Output verbose verification logs to the test output Verbose = true, PublishVerificationResults = true, ProviderVersion = "2.4.1-f3842db9e603d7", }; //Act / Assert IPactVerifier pactVerifier = new PactVerifier(config); pactVerifier.ProviderState($"{_pactServiceUri}/provider-states") .ServiceProvider("Provider", _providerUri) .HonoursPactWith("Consumer") .PactBroker("https://dius.pact.dius.com.au", uriOptions: new PactUriOptions(System.Environment.GetEnvironmentVariable("PACT_BROKER_TOKEN")), consumerVersionTags: new List <string> { "master" }) .Verify(); }
public static void Main(string[] args) { var outputter = new CustomOutputter(); var config = new PactVerifierConfig(); config.ReportOutputters.Add(outputter); IPactVerifier pactVerifier = new PactVerifier(() => { }, () => { }, config); pactVerifier.ProviderState("Get user with id '1'"); _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); _httpClient.BaseAddress = new System.Uri("http://*****:*****@"\Pact\{0}-{1}.json", ClientName, ProviderName)) .Verify(); // Assert outputter.Should().NotBeNull(); outputter.Output.Should().NotBeNullOrWhiteSpace(); outputter.Output.Should().Contain(string.Format("Verifying a Pact between {0} and {1}", ClientName, ProviderName)); outputter.Output.Should().Contain("status code 200"); System.Console.ReadLine(); }
public void EnsureProviderApiHonorsPactWithWebUi() { // Arrange var config = new PactVerifierConfig { // NOTE: We default to using a ConsoleOutput, // however xUnit 2 does not capture the console output, // so a custom outputter is required. Outputters = new List <IOutput> { new XUnitOutput(_outputHelper) }, // Output verbose verification logs to the test output Verbose = true }; //Act / Assert IPactVerifier pactVerifier = new PactVerifier(config); pactVerifier.ProviderState($"{_pactServiceUri}/provider-states") .ServiceProvider("webapi", _providerUri) .HonoursPactWith("webapp") //TODO: common location (publish to pact broker) .PactUri(@"..\..\..\..\..\pacts\webapp-webapi.json") .Verify(); }
public void EnsureCarApiHonoursPactWithConsumer() { //Arrange const string serviceUri = "http://localhost:9222"; var config = new PactVerifierConfig { Outputters = new List <IOutput> { new XUnitOutput(_output) } }; using (WebApp.Start <TestStartup>(serviceUri)) { //Act / Assert IPactVerifier pactVerifier = new PactVerifier(config); pactVerifier .ProviderState($"{serviceUri}/provider-states") .ServiceProvider("Car API", serviceUri) .HonoursPactWith("Car API Consumer") //.PactUri($"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}Consumer.Tests{Path.DirectorySeparatorChar}pacts{Path.DirectorySeparatorChar}car_api_consumer-car_api.json") .PactUri("http://localhost/pacts/provider/Car%20API/consumer/Car%20API%20Consumer/latest") .Verify(); } }
private void VerifyPactWithConsumer(JToken consumer, string pactUrl, string serviceUri) { //we need to instantiate one pact verifier for each consumer var config = new PactVerifierConfig { Outputters = new List <IOutput> { _output } }; PactUriOptions pactUriOptions = null; if (!string.IsNullOrEmpty(_configuration.PactBrokerUsername)) { pactUriOptions = new PactUriOptions(_configuration.PactBrokerUsername, _configuration.PactBrokerPassword); } var pactUri = new Uri(new Uri(_configuration.PactBrokerUri), pactUrl); var pactVerifier = new PactVerifier(config); pactVerifier .ProviderState($"{serviceUri}/provider-states") .ServiceProvider(_configuration.ProviderName, serviceUri) .HonoursPactWith(consumer.ToString()) .PactUri(pactUri.AbsoluteUri, pactUriOptions) .Verify(); }
public void EnsureConsumerHonoursPactWithpPact1Consumer() { //Arrange var config = new PactVerifierConfig(); config.ReportOutputters.Clear(); config.ReportOutputters.Add(new ConsoleOutputter(_output)); var pactVerifier = new PactVerifier(() => { }, () => { }, config); pactVerifier .ProviderState("No subject with id '8' exists") .ProviderState("Subject with id '1' exists") .ProviderState("Creating new product") ; //Act / Assert using (var testServer = TestServer.Create <TestStartup>()) { testServer.BaseAddress = new Uri("http://localhost"); pactVerifier .ServiceProvider("Something API", testServer.HttpClient) .HonoursPactWith("customerservice") .PactUri($@"..\..\..\UnitTestProject\pacts\pact1consumer-pactproducer.json") .Verify(); //NOTE: Optionally you can control what interactions are verified by specifying a providerDescription and/or providerState } }
public void EnsureDinkumCoinApiHonoursPactWithConsumer() { //Arrange const string baseUrl = "http://localhost:9011"; var fixture = new TestServerFixture(); var config = new PactVerifierConfig { Outputters = new List <IOutput> { new XUnitOutput(_output) }, Verbose = true }; using (IWebHost webHost = fixture.CreateWebHost(baseUrl)) { webHost.Start(); IPactVerifier pactVerifier = new PactVerifier(config); pactVerifier // .ProviderState($"{serviceUri}/provider-states") .ServiceProvider("dinkum-coin-api", baseUrl) .HonoursPactWith("dinkum-coin-web") .PactUri($"pacts/dinkum-coin-web-dinkum-coin-api.json") .Verify(); } }
public MessageProviderValidator( IReporter reporter, PactVerifierConfig config) { _reporter = reporter; _config = config; }
public void DoTest() { var config = new PactVerifierConfig { // NOTE: We default to using a ConsoleOutput, // however xUnit 2 does not capture the console output, // so a custom outputter is required. Outputters = new List <IOutput> { new XUnitOutput(_outputHelper) }, // Output verbose verification logs to the test output Verbose = true }; //Act / Assert IPactVerifier pactVerifier = new PactVerifier(config); pactVerifier.ProviderState($"{_pactServiceUri}/provider-states") .ServiceProvider("Provider", _providerUri) .HonoursPactWith("Consumer") .PactUri(@"..\..\..\..\..\pacts\consumer-provider.json") .Verify(); }
public MenuApiProviderTests(ITestOutputHelper output) { OutputHelper = output; ProviderUri = "http://localhost:6001"; //Get application configuration Config = Configuration.For <ConfigModel>(); //Set up the Pact configuration to be used in tests PactConfig = new PactVerifierConfig { // NOTE: We default to using a ConsoleOutput, // however xUnit 2 does not capture the console output, // so a custom outputter is required. Outputters = new List <IOutput> { new XUnitOutput(OutputHelper) }, // Output verbose verification logs to the test output Verbose = true, //If build number is present, results will be published back to the broker ProviderVersion = !string.IsNullOrEmpty(Config.Build_Number) ? Config.Build_Number : null, PublishVerificationResults = !string.IsNullOrEmpty(Config.Build_Number) }; }
public async Task ShouldThrowWhenResponseIsNotSuccessful() { var fakePactBrokerMessageHandler = new FakePactBrokerMessageHandler { ObjectToReturn = new Contract(), StatusCodeToReturn = System.Net.HttpStatusCode.BadRequest }; var config = new PactVerifierConfig { PactBrokerClient = new HttpClient(fakePactBrokerMessageHandler) { BaseAddress = new Uri("http://localhost:9292") } }; var mockConsumer = new PactVerifier(config); try { await mockConsumer.GetPactFromBroker("some/path"); } catch (PactException e) { Assert.AreEqual("Getting pact from Pact Broker failed. Pact Broker returned BadRequest", e.Message); throw; } }
public async Task PactBrokerReturnsNonSuccessStatusCode() { var fakeHttpMessageHandler = new FakePactBrokerMessageHandler { StatusCodeToReturn = System.Net.HttpStatusCode.NotFound }; var config = new PactVerifierConfig { ProviderVersion = "1.0", PactBrokerClient = new HttpClient(fakeHttpMessageHandler) { BaseAddress = new Uri("http://local-pact-broker") } }; var mockConsumer = new PactVerifier(config); try { await mockConsumer.PublishVerificationResultsAsync(_pact, new List <FailedInteraction>()); } catch (PactException e) { Assert.AreEqual("Publishing verification results failed. Pact Broker returned NotFound", e.Message); throw; } }
public BasicTests(StartupMock factory, ITestOutputHelper output) { _pactServiceUri = "http://localhost:9001"; _providerUri = "https://localhost:5090"; _outputHelper = output; factory.ConfigureRoutingMessages(t => { t.TypeBased().MapFallback("TestErrors"); }); _config = new PactVerifierConfig { Outputters = new List <IOutput> { new XUnitOutput(_outputHelper) }, Verbose = false, ProviderVersion = "1.0", //git commit PublishVerificationResults = true }; _tokenSource = new CancellationTokenSource(); factory.CreateWebHostBuilder().Build() .RunAsync(_tokenSource.Token).GetAwaiter(); _webHost = WebHost.CreateDefaultBuilder() .UseUrls(_pactServiceUri) .ConfigureServices((build, collection) => { collection.AddSingleton(factory.MongoDb); }) .UseStartup <PactStartup>() .Build(); _webHost.Start(); }
public async Task ShouldPublishVerificationResults() { var buildDirectory = AppContext.BaseDirectory; var pactDir = Path.GetFullPath($"{buildDirectory}{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}pacts{Path.DirectorySeparatorChar}"); var pactFileToReturn = File.ReadAllText(pactDir + "messageConsumer-messageProvider.json"); var fakePactBrokerMessageHandler = new FakePactBrokerMessageHandler { ObjectToReturn = JsonConvert.DeserializeObject(pactFileToReturn) }; var messageSender = new MessageSender(); var config = new PactVerifierConfig { MessageProducer = messageSender.Send, ProviderVersion = "1.0", PublishVerificationResults = true, PactBrokerClient = new HttpClient(fakePactBrokerMessageHandler) { BaseAddress = new Uri("http://localhost:9292") } }; var pactVerifier = new PactVerifier(config); await pactVerifier.VerifyPactAsync("pacts/provider/messageProvider/consumer/messageConsumer/latest"); var sentVerificationResults = JsonConvert.DeserializeObject <VerificationResults>(fakePactBrokerMessageHandler.SentRequestContents.First().Value); Assert.IsTrue(sentVerificationResults.Success); }
public void EnsureProductApiHonoursPactWithConsumer() { //Arrange const string serviceUri = "http://localhost:13607"; var config = new PactVerifierConfig { Outputters = new List <IOutput> { new CustomOutput(_output) } }; var webHostBuilder = new WebHostBuilder() .UseStartup <TestStartup>(); using (var server = new TestServer(webHostBuilder)) { //var response = server.CreateRequest($"{serviceUri}/product/productlist") // .SendAsync("GET"); //Act / Assert IPactVerifier pactVerifier = new PactVerifier(config); pactVerifier .ProviderState($"{serviceUri}/echo/status") .ServiceProvider("Product API", serviceUri) .HonoursPactWith("Product API Consumer") .PactUri($"..//consumer - driven - test//Provider//FlixOne.BookStore.ProductService.Test//pacts//product_api_consumer-product_api") //.PactUri($"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}FlixOne.BookStore.ProductService.Test{Path.DirectorySeparatorChar}pacts{Path.DirectorySeparatorChar}product_api_consumer.json") .Verify(); } }
public void EnsureEventApiHonoursPactWithConsumer() { if (!File.Exists(".\\pact\\bin\\pact-provider-verifier.bat")) { throw new Exception("Please run '.\\Build\\Download-Standalone-Core.ps1' from the project root to download the standalone provider verifier, then 'Clean' and 'Rebuild' the solution."); } //Arrange const string serviceUri = "http://localhost:9222"; var config = new PactVerifierConfig { Outputters = new List <IOutput> { new XUnitOutput(_output) } }; using (WebApp.Start <TestStartup>(serviceUri)) { //Act / Assert IPactVerifier pactVerifier = new PactVerifier(config); pactVerifier .ProviderState($"{serviceUri}/provider-states") .ServiceProvider("Event API", serviceUri) .HonoursPactWith("Event API Consumer") .PactUri("..\\..\\..\\Consumer.Tests\\pacts\\event_api_consumer-event_api.json") .Verify(); } }
public void PactProviderVerifyTest() { //set a output folder for logs and pacts retreived PactConfig = new PactVerifierConfig() { LogDir = "../../../Log" }; //set a string output to easily assert against Outputter = new CustomOutputter(); this.PactConfig.ReportOutputters.Add(Outputter); PactVerifier = new PactVerifier(() => { }, () => { }, this.PactConfig); PactFile = Path.Combine("../../../Pacts", $"{Consumer}-{Provider}.json".ToLower()); //verify the interaction PactVerifier .ProviderState("Testing Guid", setUp: GuidSetupState); //Verify will throw if there was a failure PactVerifier .MessageProvider(Provider) .HonoursPactWith(Consumer) .PactUri(PactFile) .Verify(); Assert.Contains($"Verifying a Pact between {Consumer} and {Provider}", Outputter.Output); }
public void EnsureEventApiHonoursPactWithConsumer() { //Arrange var outputter = new CustomOutputter(); var config = new PactVerifierConfig(); config.ReportOutputters.Add(outputter); IPactVerifier pactVerifier = new PactVerifier(() => {}, () => {}, config); pactVerifier .ProviderState( "there are events with ids '45D80D13-D5A2-48D7-8353-CBB4C0EAABF5', '83F9262F-28F1-4703-AB1A-8CFD9E8249C9' and '3E83A96B-2A0C-49B1-9959-26DF23F83AEB'", setUp: InsertEventsIntoDatabase) .ProviderState("there is an event with id '83f9262f-28f1-4703-ab1a-8cfd9e8249c9'", setUp: InsertEventIntoDatabase) .ProviderState("there is one event with type 'DetailsView'", setUp: EnsureOneDetailsViewEventExists); _server = TestServer.Create(app => { app.Use(typeof(AuthorizationTokenReplacementMiddleware), app.CreateDataProtector(typeof(OAuthAuthorizationServerMiddleware).Namespace, "Access_Token", "v1")); var apiStartup = new Startup(); apiStartup.Configuration(app); }); //Act / Assert pactVerifier .ServiceProvider("Event API", _server.HttpClient) .HonoursPactWith("Consumer") .PactUri("../../../Consumer.Tests/pacts/consumer-event_api.json") .Verify(); // Verify that verifaction log is also sent to additional reporters defined in the config Assert.Contains("Verifying a Pact between Consumer and Event API", outputter.Output); }
public void EnsureUserApiHonoursPactWithBilling() { // Arrange var config = new PactVerifierConfig { // NOTE: We default to using a ConsoleOutput, // however xUnit 2 does not capture the console output, // so a custom outputter is required. Outputters = new List <IOutput> { new XUnitOutput(_outputHelper) }, // Output verbose verification logs to the test output Verbose = true }; //Act / Assert IPactVerifier pactVerifier = new PactVerifier(config); //pactVerifier.ProviderState($"{PactUri}/provider-states"); pactVerifier .ServiceProvider(ProviderName, ProviderUri) .HonoursPactWith(Consumer) .PactUri($@"C:\Users\chandan.vasishta\source\repos\User.Solution\Pacts\{Consumer}-{ProviderName}.json") ////or //.PactUri("http://pact-broker/pacts/provider/Something%20Api/consumer/Consumer/latest") //You can specify a http or https uri ////or //.PactUri("http://pact-broker/pacts/provider/Something%20Api/consumer/Consumer/latest", new PactUriOptions("someuser", "somepassword")) //You can also specify http/https basic auth details ////or //.PactUri("http://pact-broker/pacts/provider/Something%20Api/consumer/Consumer/latest", new PactUriOptions("sometoken")) //Or a bearer token ////or (if you're using the Pact Broker, you can use the various different features, including pending pacts) //.PactBroker("http://pact-broker", uriOptions: new PactUriOptions("sometoken"), enablePending: true, consumerVersionTags: new List<string> { "master" }, providerVersionTags: new List<string> { "master" }, consumerVersionSelectors: new List<VersionTagSelector> { new VersionTagSelector("master", false, true) }) .Verify(); }
public async Task HonourPactWithSpyLens() { var baseAddress = $"http://*****:*****@"c:\\git\\pacts\\spylens_frontend-spymaster_api.json") .Verify(); await webHost.StopAsync(); }
/// <summary> /// Function to be used within a test to execute a call to the pact broker to verify the API /// </summary> /// <param name="outputters"></param> public void RunPactBrokerTest(IList <IOutput> outputters = null) { if (outputters is null) { outputters = new List <IOutput>(); } if (!outputters.Any(x => x.GetType() == typeof(ConsoleOutput))) { outputters.Add(new ConsoleOutput()); } var pactVerifierConfig = new PactVerifierConfig { Outputters = outputters }; var user = Configuration.GetValue <string>(Constants.ENV_VAR_PACT_BROKER_USER); var pwd = Configuration.GetValue <string>(Constants.ENV_VAR_PACT_BROKER_USER_PASSWORD); var pactUriOptions = new PactUriOptions().SetBasicAuthentication(user, pwd); var name = Configuration.GetValue <string>(Constants.ENV_VAR_PACT_BROKER_PROVIDER_NAME); var path = Configuration.GetValue <string>(Constants.ENV_VAR_PACT_BROKER_PATH); IPactVerifier pactVerifier = new PactVerifier(pactVerifierConfig); pactVerifier .ServiceProvider(name, ServerUri) .PactBroker(path, pactUriOptions) .ProviderState(ServerUri + Constants.PROVIDER_STATES_ROUTE) .Verify(); }
public async Task ShouldThrowWhenClientThrowsForAnyOtherReason() { var fakePactBrokerMessageHandler = new FakePactBrokerMessageHandler { ObjectToReturn = new Contract(), ExceptionToThrow = new HttpRequestException("Something went wrong.") }; var config = new PactVerifierConfig { PactBrokerClient = new HttpClient(fakePactBrokerMessageHandler) { BaseAddress = new Uri("http://localhost:9292") } }; var mockConsumer = new PactVerifier(config); try { await mockConsumer.GetPactFromBroker("some/path"); } catch (PactException e) { Assert.AreEqual("Pact cannot be retrieved using the provided Pact Broker Client: Something went wrong.", e.Message); throw; } }
public void EnsureProviderApiHonoursPactWithConsumer() { // Arrange config = new PactVerifierConfig { Outputters = new List <IOutput> { new XUnitOutput(_outputHelper) }, Verbose = true }; config.CustomHeader = new KeyValuePair <string, string>("X-Clarksons-Security-Cloud", "UqkejnjkHNM5gPY4VKeLNeoNv2eLUvZL8Di1xDqjc/I1dvdcQO9EGeUg6wYR0+ta+218kbu5Z5GgodKF92WmuGyTZGZ600fAS0OPKZ2kXIiwwqVO+a2apqxIOYLLrJdOFGkw6h6pZ8NurSQdQYvavA=="); //Act / Assert pactVerifier = new PactVerifier(config); pactVerifier.ProviderState($"{_pactServiceUri}/provider-states") .ServiceProvider("fixture_api", _providerUri) .HonoursPactWith("EventAPIConsumer") .PactUri(@"http://104.214.219.231/pacts/provider/OperationServices/consumer/EventAPIConsumer/latest"); pactVerifier.Verify(); }
public void Ctor_WhenCalled_SetsOutputters() { var verifierConfig = new PactVerifierConfig(); var config = GetSubject(verifierConfig: verifierConfig); Assert.Equal(verifierConfig.Outputters, config.Outputters); }
public void EnsureTheThingHonorsPactWithConsumer() { // arrange const string serviceUri = "http://*****:*****@"C:\temp\pact\pactDir\pactconsumer-pactproducer.json") .Verify(); } }
public async Task EnsureProviderApiHonoursPactWithConsumerAsync() { // Arrange var config = new PactVerifierConfig { // NOTE: We default to using a ConsoleOutput, // however xUnit 2 does not capture the console output, // so a custom outputter is required. Outputters = new List <IOutput> { new XUnitOutput(_outputHelper) }, // Output verbose verification logs to the test output Verbose = true }; var tcs = new TaskCompletionSource <bool>(); // complete task in event //tcs.SetResult(fa); // wait for task somewhere else await tcs.Task; /* * //Act / Assert * IPactVerifier pactVerifier = new PactVerifier(config); * pactVerifier.ProviderState($"{_pactServiceUri}/provider-states") * .ServiceProvider("Provider", _providerUri) * .HonoursPactWith("Consumer") * .PactUri(@"..\..\..\..\..\pacts\consumer-provider.json") * .Verify();*/ }
public async Task HonourPactWithSpyLens() { var baseAddress = $"http://*****:*****@"http://localhost:8082/pacts/provider/SpyMaster%20Api/consumer/SpyLens%20JS%20Frontend/latest") .Verify(); await webHost.StopAsync(); }
public Reporter(PactVerifierConfig config) : this(new List<Action<string>> { Console.WriteLine, new FileReportOutputter(config.LoggerName).Write }.Concat(config.Reporters).ToList()) { }
public ProviderServiceValidator( IHttpRequestSender httpRequestSender, IReporter reporter, PactVerifierConfig config) : this(new ProviderServiceResponseComparer(), httpRequestSender, reporter, config) { }
internal ProviderServiceValidator( IProviderServiceResponseComparer providerServiceResponseComparer, IHttpRequestSender httpRequestSender, IReporter reporter, PactVerifierConfig config) { _providerServiceResponseComparer = providerServiceResponseComparer; _httpRequestSender = httpRequestSender; _reporter = reporter; _config = config; }