StitchError: service not found: 'mongodb-atlas' - mongodb-stitch

I am having a problem getting data in android from atlas mongoDB (stitch):
service not found: 'mongodb-atlas'
StitchError: service not found: 'mongodb-atlas'
{
"arguments": [
{
"database": "vutuduDB",
"collection": "test",
"query": {},
"limit": {
"$numberInt": "0"
},
"project": null,
"sort": null
}
],
"name": "find",
"service": "mongodb-atlas"
}
Also when I tried to create a http service and use the console I got the same problem while running the line:
context.services.get("mongodb-atlas");
Java code:
public class SearchFragment extends Fragment {
Button btnSearch;
private StitchAppClient stitchClient;
private RemoteMongoClient mongoClient;
private RemoteMongoCollection itemsCollection;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View layout = inflater.inflate(R.layout.search_fragment_layout, container, false);
this.stitchClient = Stitch.getDefaultAppClient();
Log.d("stitch", "logging in anonymously");
stitchClient.getAuth().loginWithCredential(new AnonymousCredential()
).continueWithTask(new Continuation<StitchUser, Task<Void>>() {
#Override
public Task<Void> then(#NonNull Task<StitchUser> task) throws Exception {
if (task.isSuccessful()) {
Log.d("stitch", "logged in anonymously as user " + task.getResult());
} else {
Log.e("stitch", "failed to log in anonymously", task.getException());
}
mongoClient = stitchClient.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas");
RemoteMongoDatabase db = mongoClient.getDatabase("vutuduDB");
Log.d("stitch", "GETTING ITEMS");
db.getCollection("test").find().forEach(item -> Log.d("ITEM: ", item.toString()));
return null;
}
});
return layout;
}
}

Related

Not all Information is appearing in Kibana (Serilog)

When I run my AspNetCore V5 app I only see logging appear in Kibana up to the point I call CreateHostBuilder(args).
So, in Kibana I see
Step 1
and
Starting app
But nothing after that. Even if I add Log.Information("Requested data"); in a controller method that is being executed, I don't see it.
appsettings.json
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Warning"
}
}
},
"ElasticConfiguration": {
"Uri": "http://localhost:9200"
},
"AllowedHosts": "*"
}
Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Sinks.Elasticsearch;
using System;
using System.Reflection;
namespace BlazorElkSerilogTest.Server
{
public class Program
{
public static void Main(string[] args)
{
//configure logging first
ConfigureLogging();
Log.Information("Step {step}", 1);
IHostBuilder hostBuilder = CreateHostBuilder(args);
Log.Information("Step {step}", 2);
IHost host = hostBuilder.Build();
Log.Information("Step {step}", 3);
host.Run();
}
static void ConfigureLogging()
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile(
$"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json",
optional: true)
.Build();
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.WriteTo.Debug()
.WriteTo.Console()
.WriteTo.Elasticsearch(ConfigureElasticSink(configuration, environment))
.Enrich.WithProperty("Environment", environment)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
static ElasticsearchSinkOptions ConfigureElasticSink(IConfigurationRoot configuration, string environment)
{
return new ElasticsearchSinkOptions(new Uri(configuration["ElasticConfiguration:Uri"]))
{
AutoRegisterTemplate = true,
IndexFormat = $"{Assembly.GetExecutingAssembly().GetName().Name.ToLower().Replace(".", "-")}-{environment?.ToLower().Replace(".", "-")}-{DateTime.UtcNow:yyyy-MM}"
};
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
try
{
Log.Information("Starting app");
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureAppConfiguration(configuration =>
{
configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
configuration.AddJsonFile(
$"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json",
optional: true);
})
.UseSerilog();
}
catch (Exception e)
{
Log.Fatal(e, "Application startup failed");
throw;
}
finally
{
Log.CloseAndFlush();
}
}
}
}
The try/catch/finally should wrap the code in Main, not CreateHostBuilder - otherwise the logger is closed before the app runs!

User can access resource even if he's not part of "#Secured([role])"

I would like to secure my endpoint so only users with the role READ can access a certain resource. Those are my configurations:
Controller:
#RestController
#RequestMapping("/api/status")
public class StatusController {
#RequestMapping(method = RequestMethod.GET)
#Secured("READ")
Map<String, Object> getSecureStatus() {
Map<String, Object> statusMap = new LinkedHashMap<>();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
statusMap.put("auth", auth);
return statusMap;
}
}
The WebSecurityConfigurerAdapter:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// .antMatchers("/").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.httpBasic();
}
}
GlobalAuthenticationConfigurerAdapter:
#Configuration
public class AuthenticationManagerConfig extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("teddy").password("password").roles("USER");
}
}
I would assume that Teddy shouldn't be able to access the resource, as his role is USER rather than READ.
But with this call, Teddy gets his information anyway:
curl -u teddy:password 'http://localhost:8080/api/status/':
{
"auth": {
"details": {
"remoteAddress": "127.0.0.1",
"sessionId": null
},
"authorities": [
{
"authority": "ROLE_USER"
}
],
"authenticated": true,
"principal": {
"password": null,
"username": "teddy",
"authorities": [
{
"authority": "ROLE_USER"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "teddy"
}
}
What am I missing?
Edit: removed .antMatchers("/").permitAll()
Probably it's because you're using .antMatchers("/").permitAll() it's telling spring that you're allowing every request.
Try removing it from your configuration.
I found the mistake. I overlooked that getSecureStatus() wasn't explicitely defined public. This code fixes it:
#RestController
#RequestMapping("/api/status")
public class StatusController {
#RequestMapping(method = RequestMethod.GET)
#Secured("READ")
public Map<String, Object> getSecureStatus() {
Map<String, Object> statusMap = new LinkedHashMap<>();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
statusMap.put("auth", auth);
return statusMap;
}
}

How to custom Principle object with Spring Cloud Security?

I finally got my oauth2 server running.
From command line, if I run
curl -s -u acme:acmesecret -d grant_type=password -d username=myusername -d password=mypassword -H Accept:application/json http://localhost:9999/oauth/token
I got result below,
{
"access_token":"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0Mzk1NDU3ODAsInVzZXJfbmFtZSI6IisxIDQwODUxODIxMTUiLCJhdXRob3JpdGllcyI6WyJVU0VSIiwiQURNSU4iXSwianRpIjoiYmFkMDgyMjctNDExNC00OTZkLWE1NDMtYzBhMjc3YTBhZDkzIiwiY2xpZW50X2lkIjoiYWNtZSIsInNjb3BlIjpbIndlYnNob3AiXX0.CM_0gBHVyecOMmpc2cnKTus48PNv8gfHDyzVOVa5TBDxv4QlnDO93otmUs86IQqPaqaI133tT1NPU0pt2dbV5lrY3FOlPFXB0zZw5ptIXCtpaQLgl3e9hkB1aSfv3YxbHiOV8n3FcvNdz9Ihi9XEQdzqT8YfK7mCeMOjdb1i6Ve9axwjJI9ZHxXzDMcJsnYBcQCKG52G3-rWzgzlaQkPZY6mO7q0eO0jgVWthLfSBumHlDt9QXaBkETH3CRHxSuJqlo4J3TZxP4-1vPLkgh8Ku2rY5A9rT-xOKG8_5s2CJduCZt0qQrXZhz7sk0m2IdxDDwXumPv6zyHyD2J3sjHUA",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX25hbWUiOiIrMSA0MDg1MTgyMTE1Iiwic2NvcGUiOlsid2Vic2hvcCJdLCJhdGkiOiJiYWQwODIyNy00MTE0LTQ5NmQtYTU0My1jMGEyNzdhMGFkOTMiLCJleHAiOjE0NDIwOTQ1ODAsImF1dGhvcml0aWVzIjpbIlVTRVIiLCJBRE1JTiJdLCJqdGkiOiJjYWNkOWEzOC1mOWE5LTQ4NjAtOWZmMi05NWMzMzU4MmY0NDAiLCJjbGllbnRfaWQiOiJhY21lIn0.DhaqIEdYWR2VPkgh72bQ17ZLqcVVfdYtT8DdKibjIcZUTNNjN_atdyKYKNEtdSyEES-ArHL0jCVXUg3EKiut_qtvn8oaLYEAxCNfztHyo_b-RZIxOgr71m82n66vSwRzxQnoKcGltxpZs-PK5p-gmbaEWK4EO63AkJpgN_IrIGV4eVQmidanz53rvq-CBiq-1FFb64OilesUxkSPOVkbb-q-mUmd8EG4khdbf44LD9VhyZwt8lOOi8NnksnnGhogiynU9p7tirAv6w_g8IO7uy06fWaLyn6rAgPga3CYgo9ggFIICWKn-QFipkHgiehq6y_1-xTGlgHnRKXcnPIZcg",
"expires_in": 34996,
"scope": "webshop",
"jti": "bad08227-4114-496d-a543-c0a277a0ad93"
}
With the token returned, I can get user information with a curl command. You can a lot of user information in the response.
curl http://localhost:9999/user -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0Mzk1NDU3ODAsInVzZXJfbmFtZSI6IisxIDQwODUxODIxMTUiLCJhdXRob3JpdGllcyI6WyJVU0VSIiwiQURNSU4iXSwianRpIjoiYmFkMDgyMjctNDExNC00OTZkLWE1NDMtYzBhMjc3YTBhZDkzIiwiY2xpZW50X2lkIjoiYWNtZSIsInNjb3BlIjpbIndlYnNob3AiXX0.CM_0gBHVyecOMmpc2cnKTus48PNv8gfHDyzVOVa5TBDxv4QlnDO93otmUs86IQqPaqaI133tT1NPU0pt2dbV5lrY3FOlPFXB0zZw5ptIXCtpaQLgl3e9hkB1aSfv3YxbHiOV8n3FcvNdz9Ihi9XEQdzqT8YfK7mCeMOjdb1i6Ve9axwjJI9ZHxXzDMcJsnYBcQCKG52G3-rWzgzlaQkPZY6mO7q0eO0jgVWthLfSBumHlDt9QXaBkETH3CRHxSuJqlo4J3TZxP4-1vPLkgh8Ku2rY5A9rT-xOKG8_5s2CJduCZt0qQrXZhz7sk0m2IdxDDwXumPv6zyHyD2J3sjHUA"
{
"details": {
"remoteAddress": "127.0.0.1",
"sessionId": null,
"tokenValue": "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0Mzk1NDU3ODAsInVzZXJfbmFtZSI6IisxIDQwODUxODIxMTUiLCJhdXRob3JpdGllcyI6WyJVU0VSIiwiQURNSU4iXSwianRpIjoiYmFkMDgyMjctNDExNC00OTZkLWE1NDMtYzBhMjc3YTBhZDkzIiwiY2xpZW50X2lkIjoiYWNtZSIsInNjb3BlIjpbIndlYnNob3AiXX0.CM_0gBHVyecOMmpc2cnKTus48PNv8gfHDyzVOVa5TBDxv4QlnDO93otmUs86IQqPaqaI133tT1NPU0pt2dbV5lrY3FOlPFXB0zZw5ptIXCtpaQLgl3e9hkB1aSfv3YxbHiOV8n3FcvNdz9Ihi9XEQdzqT8YfK7mCeMOjdb1i6Ve9axwjJI9ZHxXzDMcJsnYBcQCKG52G3-rWzgzlaQkPZY6mO7q0eO0jgVWthLfSBumHlDt9QXaBkETH3CRHxSuJqlo4J3TZxP4-1vPLkgh8Ku2rY5A9rT-xOKG8_5s2CJduCZt0qQrXZhz7sk0m2IdxDDwXumPv6zyHyD2J3sjHUA",
"tokenType": "Bearer",
"decodedDetails": null
},
"authorities": [
{
"authority": "USER"
},
{
"authority": "ADMIN"
}
],
"authenticated": true,
"userAuthentication": {
"details": {
"grant_type": "password",
"username": "myusername"
},
"authorities": [
{
"authority": "USER"
},
{
"authority": "ADMIN"
}
],
"authenticated": true,
"principal": {
"id": "usr000d11b4c86-13ba-11e5-b905-56847afe9799",
"json": null,
"version": 0,
"created": 1434412879774,
"updated": 1438877901186,
"info": {
"nickName": "Kevin",
"country": "China",
"zipcode": null,
"state": null,
"city": "",
"occupation": "",
"gender": null,
"imgPath": "https://ddbs0erhouflt.cloudfront.net/mcf000ecd36bcb-f33e-4d50-9102-7a8706b45eb8",
"about": "",
"dueDate": 1447312895201,
"birthday": 0
},
"privateInfo": {
"email": "zyj#yahoo.com",
"phone": "myusername",
"password": "f45206ce4247b5d9af350d4600adc85c",
"tempPassword": null,
"tokens": null
},
"settings": null,
"type": "Super",
"status": "Active",
"enabled": true,
"username": "myusername",
"password": "f45206ce4247b5d9af350d4600adc85c",
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"authorities": [
{
"authority": "USER"
},
{
"authority": "ADMIN"
}
]
},
"credentials": null,
"name": "myusername"
},
"credentials": "",
"oauth2Request": {
"clientId": "acme",
"scope": [
"webshop"
],
"requestParameters": {
"grant_type": "password",
"username": "myusername"
},
"resourceIds": [],
"authorities": [],
"approved": true,
"refresh": false,
"redirectUri": null,
"responseTypes": [],
"extensions": {},
"grantType": "password",
"refreshTokenRequest": null
},
"principal": {
"id": "usr000d11b4c86-13ba-11e5-b905-56847afe9799",
"json": null,
"version": 0,
"created": 1434412879774,
"updated": 1438877901186,
"info": {
"nickName": "Kevin",
"country": "China",
"zipcode": null,
"state": null,
"city": "",
"occupation": "",
"gender": null,
"imgPath": "https://ddbs0erhouflt.cloudfront.net/mcf000ecd36bcb-f33e-4d50-9102-7a8706b45eb8",
"about": "",
"dueDate": 1447312895201,
"birthday": 0
},
"privateInfo": {
"email": "zyj#yahoo.com",
"phone": "myusername",
"password": "f45206ce4247b5d9af350d4600adc85c",
"tempPassword": null,
"tokens": null
},
"settings": null,
"type": "Super",
"status": "Active",
"enabled": true,
"username": "myusername",
"password": "f45206ce4247b5d9af350d4600adc85c",
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"authorities": [
{
"authority": "USER"
},
{
"authority": "ADMIN"
}
]
},
"clientOnly": false,
"name": "myusername"
}
I have a Spring boot micro service client. It uses spring-cloud-security. One of the web service was below,
#RequestMapping(value="getsth", method=RequestMethod.GET)
public SomeObject getsth(Principal principal) {
....
}
When method getsth is called, I can see an object of OAuth2Authentication was passed in. However, user information like user id, user phone number are missing.
My question: how can I get all the user information? Is there any way to custom the principal object?
Thanks,
I was struggling with the same issue. I got it to work however.
I'm using JWT so might be slightly different to what you're doing, but the concept is the same.
First of all, I created a custom TokenServices to get the extra information out of the user and added it to the authentication object:
public class TafTokenServices extends DefaultTokenServices {
#Override
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
final TafUserDetails tafUserDetails = (TafUserDetails)authentication.getPrincipal();
final Map<String, Object> tafInfo = new HashMap<>();
tafInfo.put("EMAIL", tafUserDetails.getEmailAddress());
authentication.setDetails(tafInfo);
return super.createAccessToken(authentication);
}
}
Then configure your auth server to use it:
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).accessTokenConverter(jwtAccessTokenConverter())
.tokenServices(tafTokenServices());
}
#Bean
public AuthorizationServerTokenServices tafTokenServices() {
final TafTokenServices tafTokenServices = new TafTokenServices();
final JwtTokenStore jwtTokenStore = new JwtTokenStore(this.jwtAccessTokenConverter());
tafTokenServices.setTokenStore(jwtTokenStore);
tafTokenServices.setTokenEnhancer(this.jwtAccessTokenConverter());
return tafTokenServices;
}
Also in the auth server you need to then transfer the data out of the authentication object into the token with your own AccessTokenCoverter object:
public class TafJwtAccessTokenConverter extends JwtAccessTokenConverter {
private static final String EMAIL_KEY = "EMAIL";
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
final Map<String, Object> authDetails = (Map<String, Object>)authentication.getDetails();
((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(authDetails);
return super.enhance(accessToken, authentication);
}
#Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
final OAuth2Authentication authentication = super.extractAuthentication(map);
final Map<String, String> details = new HashMap<>();
details.put(EMAIL_KEY, (String)map.get(EMAIL_KEY));
authentication.setDetails(details);
return authentication;
}
}
NOTE: the enhance() method is called during creation of the token, so you need this in the auth server.
NOTE: extractAuthentication() is called in the downstream services during authentication, so this implementation needs to exist there as well. You need to configure your resource server to use this AccessTokenConverter.
That will get the info into the token to pass downstream. Note I didn't want to keep the data in the custom user object because I don't want to have my other services depend on that object.
The next step is to get the stuff out of the token and use it in your resource server. You do this by overriding extractAuthentication() to get the details from the map and put them into the authentication object. They will now be available in your application by doing something like this:
private String getEmail() {
final OAuth2Authentication auth = this.getAuthentication();
final OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)auth.getDetails();
final Map<String, Object> map = (Map)details.getDecodedDetails();
return "Your email address is " + map.get("EMAIL");
}
private OAuth2Authentication getAuthentication() {
return (OAuth2Authentication)SecurityContextHolder.getContext().getAuthentication();
}
I'm not sure this is right way to go, it's a bit fiddly. I have an issue open here where there is a discussion about it:
https://github.com/spring-cloud/spring-cloud-security/issues/85#issuecomment-165498497
to customize it you have to provide your own UserDetailsService class:
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username);
}
}
where User is your entity with all fields you require:
public class User implements Serializable, UserDetails {...
then you have to configure spring security to actually use your custom user details service. Something similar to:
#Configuration
#EnableWebMvcSecurity
#EnableGlobalAuthentication
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyUserDetailsService myUserDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.parentAuthenticationManager(authenticationManager)
.userDetailsService(myUserDetailsService);
}

APIs not getting detected by swagger on a Spring ROO project

I have tried a lot of things but APIs are not getting detected by swagger for some reason. Do i have to specify a package for swagger to scan? or some url include patterns?
My Swager Config :
#Configuration
#EnableSwagger
#EnableWebMvc
public class SwaggerConfiguration {
private final Logger log = LoggerFactory
.getLogger(SwaggerConfiguration.class);
/**
* Swagger Spring MVC configuration.
*/
#Bean
public SwaggerSpringMvcPlugin swaggerSpringMvcPlugin(
SpringSwaggerConfig springSwaggerConfig) {
log.debug("Starting Swagger");
StopWatch watch = new StopWatch();
watch.start();
SwaggerSpringMvcPlugin swaggerSpringMvcPlugin = new SwaggerSpringMvcPlugin(
springSwaggerConfig).apiInfo(apiInfo())
.genericModelSubstitutes(ResponseEntity.class);
swaggerSpringMvcPlugin.build();
watch.stop();
log.debug("Started Swagger in {} ms", watch.getTotalTimeMillis());
return swaggerSpringMvcPlugin;
}
/**
* API Info as it appears on the swagger-ui page.
*/
private ApiInfo apiInfo() {
return new ApiInfo("Title", "Description", "terms of service",
"contact", "license", "licenseUrl");
}
}
Sample Controller
#RequestMapping("/settings")
#Controller
#Api(value = "/settings", description = "Endpoint for settings management")
public class SettingsController {
#ApiOperation(value = "API Operation")
#RequestMapping(value = "/changepassword", method = RequestMethod.POST)
public #ResponseBody Map<String, Object> changePassword(#RequestParam Map<String, String> userProperties,
Model model, HttpServletRequest httpServletRequest, Locale locale) {
Map<String, Object> responseMap = new HashMap<String, Object>();
return responseMap;
}
}
I get an empty response
{
"apiVersion": "1.0",
"swaggerVersion": "1.2",
"apis": [ ],
"authorizations": [ ],
"info":
{
"title": "Title",
"description": "Description",
"termsOfServiceUrl": "terms of service",
"contact": "contact",
"license": "license",
"licenseUrl": "licenseUrl"
}
}
I am using swagger-springmvc version 1.0.2 and spring version 4.1.6.RELEASE
Follow the instructions in the following URL :
http://naddame.blogspot.in/2014/12/spring-roo-mvc-integration-for-swagger.html

ModelState null when Controller instantiated by unit test

I'm trying to unit test my controller which contains a check for ModelState.IsValid but for some reason whatever I do I always end up with an null exception everywhere I try to access the ModelState (both in unit test and controller function).
Everywhere I check people just use the ModelState.Clear()/.AddModelError() and it seems to work for them. Some have said that they get an exception when the mvc versions differ but I have checked that and they were the same.
What can I be missing?
Heres the unit test code:
private Mock<IRegistrationService> registrationService;
private RegistrationController registrationCtrl;
public RegisteringANewUser()
{
registrationService = new Mock<IRegistrationService>();
registrationCtrl = new RegistrationController(registrationService.Object);
registrationCtrl.ModelState.Clear(); <- throws exception
}
[Fact]
public void ShouldRegisterUser_WhenInputIsCorrect()
{
var registration = RegistrationHelper.CreateRegistrationVM("username", "password", "asfa#asf.com");
registrationCtrl.Post(registration);
registrationService.Verify(s => s.Register(registration), Times.Once);
}
[Fact]
public void ShouldReturnBadRequest_WhenInputIsInvalid()
{
var registration = RegistrationHelper.CreateRegistrationVM("", "", "");
registrationCtrl.ModelState.AddModelError("Error", "Error"); <- throws exception
var result = registrationCtrl.Post(registration);
Assert.Equal((int)HttpStatusCode.BadRequest, result.StatusCode);
}
The controller function under test:
public HttpStatusCodeResult Post(RegistrationVM registration)
{
if (!ModelState.IsValid) <- throws exception
{
return new HttpStatusCodeResult((int)HttpStatusCode.BadRequest);
}
_registrationService.Register(registration);
return new HttpStatusCodeResult((int)HttpStatusCode.OK);
}
project.json for unit test library:
{
"version": "1.0.0-*",
"dependencies": {
"Web": "1.0.0-*",
"Moq": "4.2.1409.1722",
"Xunit.KRunner": "1.0.0-rc1-10618",
"Microsoft.AspNet.Mvc": "6.0.0-beta1"
},
"frameworks": {
"aspnet50": {
"dependencies": {
}
}
},
"commands": {
"test": "Xunit.KRunner"
}
}
And for the web site project:
{
/* Click to learn more about project.json http://go.microsoft.com/fwlink/?LinkID=517074 */
"webroot": "wwwroot",
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNet.Mvc": "6.0.0-beta1",
//"Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-beta1",
"Microsoft.AspNet.Diagnostics": "1.0.0-beta1",
"Microsoft.AspNet.Security.Cookies": "1.0.0-beta1",
"Microsoft.AspNet.Server.IIS": "1.0.0-beta1",
"Microsoft.AspNet.Server.WebListener": "1.0.0-beta1",
"Microsoft.AspNet.StaticFiles": "1.0.0-beta1",
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta1",
"Microsoft.Framework.CodeGenerators.Mvc": "1.0.0-beta1",
"Microsoft.Framework.Logging": "1.0.0-beta1",
"Microsoft.Framework.Logging.Console": "1.0.0-beta1",
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-beta1",
"mongocsharpdriver": "1.10.0.0-rc1"
},
"commands": {
/* Change the port number when you are self hosting this application */
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000",
"gen": "Microsoft.Framework.CodeGeneration"
},
"frameworks": {
"aspnet50": { },
"aspnetcore50": { } <- tried to remove this as I dont have it in my unit test lib without success
},
"exclude": [
"wwwroot",
"node_modules",
"bower_components"
],
"packExclude": [
"node_modules",
"bower_components",
"**.kproj",
"**.user",
"**.vspscc"
],
"scripts": {
"postrestore": [ "npm install" ],
"prepare": [ "grunt bower:install" ]
}
}
This was indeed a gap (until beta3). Was tracked by this Issue
In Beta3 release (or you can just get at the dev builds in a day or so), we are going to initialize a unit testing only ViewDataDictionary so that your unit test will get an empty ModelState.
Here is a the new code snippet from Controller.cs
[Activate]
public ViewDataDictionary ViewData
{
get
{
if (_viewData == null)
{
// This should run only for the controller unit test scenarios
_viewData =
new ViewDataDictionary(new EmptyModelMetadataProvider(),
ActionContext?.ModelState ?? new ModelStateDictionary());
}
return _viewData;
}
set
{
if (value == null)
{
throw
new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(ViewData));
}
_viewData = value;
}
}
And (no change to the code)
public ModelStateDictionary ModelState
{
get
{
return ViewData?.ModelState;
}
}

Resources