authentication-system
This commit is contained in:
parent
82471ecd52
commit
b0b200cb0a
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"blurb": "Learn about the const and readonly keywords by refactoring an authentication system.",
|
||||
"contributors": [
|
||||
"ErikSchierboom",
|
||||
"yzAlvin"
|
||||
],
|
||||
"authors": [
|
||||
"mikedamay"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"AuthenticationSystem.cs"
|
||||
],
|
||||
"test": [
|
||||
"AuthenticationSystemTests.cs"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/Exemplar.cs"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"track":"csharp","exercise":"authentication-system","id":"f06b3c9cc6b24910ae8bb67581d1b6e2","url":"https://exercism.org/tracks/csharp/exercises/authentication-system","handle":"benharri","is_requester":true,"auto_approve":false}
|
|
@ -0,0 +1,53 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
public class Authenticator
|
||||
{
|
||||
private class EyeColor
|
||||
{
|
||||
public string Blue = "blue";
|
||||
public string Green = "green";
|
||||
public string Brown = "brown";
|
||||
public string Hazel = "hazel";
|
||||
public string Brey = "grey";
|
||||
}
|
||||
|
||||
public Authenticator(Identity admin)
|
||||
{
|
||||
this.admin = admin;
|
||||
}
|
||||
|
||||
private Identity admin;
|
||||
|
||||
private readonly IDictionary<string, Identity> developers
|
||||
= new Dictionary<string, Identity>
|
||||
{
|
||||
["Bertrand"] = new Identity
|
||||
{
|
||||
Email = "bert@ex.ism",
|
||||
EyeColor = "blue"
|
||||
},
|
||||
|
||||
["Anders"] = new Identity
|
||||
{
|
||||
Email = "anders@ex.ism",
|
||||
EyeColor = "brown"
|
||||
}
|
||||
};
|
||||
|
||||
public Identity Admin
|
||||
{
|
||||
get { return admin; }
|
||||
}
|
||||
|
||||
public IDictionary<string, Identity> GetDevelopers()
|
||||
{
|
||||
return developers;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Identity
|
||||
{
|
||||
public string Email { get; set; }
|
||||
|
||||
public string EyeColor { get; set; }
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="Exercism.Tests" Version="0.1.0-beta1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
using Exercism.Tests;
|
||||
|
||||
public class AuthenticationSystemTests
|
||||
{
|
||||
[Fact]
|
||||
[Task(4)]
|
||||
public void GetAdmin()
|
||||
{
|
||||
var admin = new Identity { EyeColor = "green", Email = "admin@ex.ism" };
|
||||
var authenticator = new Authenticator(admin);
|
||||
Assert.Equal(admin, authenticator.Admin);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(5)]
|
||||
public void GetDevelopers()
|
||||
{
|
||||
var authenticator = new Authenticator(new Identity { EyeColor = "green", Email = "admin@ex.ism" });
|
||||
var devs = authenticator.GetDevelopers() as IDictionary<string, Identity>;
|
||||
bool?[] actual = { devs != null, devs?.Count == 2, devs?["Anders"].EyeColor == "brown" };
|
||||
bool?[] expected = { true, true, true };
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
You can run the tests by opening a command prompt in the exercise's directory, and then running the [`dotnet test` command](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test)
|
||||
Alternatively, most IDE's have built-in support for running tests, including [Visual Studio](https://docs.microsoft.com/en-us/visualstudio/test/run-unit-tests-with-test-explorer), [Rider](https://www.jetbrains.com/help/rider/Unit_Testing_in_Solution.html) and [Visual Studio code](https://github.com/OmniSharp/omnisharp-vscode/wiki/How-to-run-and-debug-unit-tests).
|
||||
See the [tests page](https://exercism.io/tracks/csharp/tests) for more information.
|
||||
|
||||
## Skipped tests
|
||||
|
||||
Initially, only the first test will be enabled.
|
||||
This is to encourage you to solve the exercise one step at a time.
|
||||
Once you get the first test passing, remove the `Skip` property from the next test and work on getting that test passing.
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit AuthenticationSystem.cs` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [C# track's documentation](https://exercism.org/docs/tracks/csharp)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
To get help if you're having trouble, you can use one of the following resources:
|
||||
|
||||
- [Gitter](https://gitter.im/exercism/xcsharp) is Exercism C# track's Gitter room; go here to get support and ask questions related to the C# track.
|
||||
- [/r/csharp](https://www.reddit.com/r/csharp) is the C# subreddit.
|
||||
- [StackOverflow](http://stackoverflow.com/questions/tagged/c%23) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
|
|
@ -0,0 +1,32 @@
|
|||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- [Readonly fields][readonly-fields]: how to define a readonly field.
|
||||
- [Constants][constants]: how to define constants.
|
||||
|
||||
## 1. Set appropriate fields and properties to const
|
||||
|
||||
- Constants in C# are discussed [here][constants].
|
||||
|
||||
## 2. Set appropriate fields to readonly
|
||||
|
||||
- Read-only fields are discussed [here][readonly-fields].
|
||||
|
||||
## 3. Remove set accessor or make it private for any appropriate field
|
||||
|
||||
- This [article][properties] discusses C# properties.
|
||||
|
||||
## 4. Ensure that the admin cannot be tampered with
|
||||
|
||||
- See [this][defensive-copying] discussion on the pattern and purpose of defensive copying.
|
||||
|
||||
## 5. Ensure that the developers cannot be tampered with
|
||||
|
||||
- Read-only dictionaries are discussed [here][readonly-dictionaries].
|
||||
|
||||
[readonly-fields]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly#readonly-field-example
|
||||
[constants]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/constants
|
||||
[properties]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties
|
||||
[defensive-copying]: https://www.informit.com/articles/article.aspx?p=31551&seqNum=2
|
||||
[readonly-dictionaries]: https://docs.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.readonlydictionary-2?view=netcore-3.1
|
|
@ -0,0 +1,88 @@
|
|||
# Authentication System
|
||||
|
||||
Welcome to Authentication System on Exercism's C# Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
## Constants
|
||||
|
||||
The `const` modifier can be (and generally should be) applied to any field where its value is known at compile time and will not change during the lifetime of the program.
|
||||
|
||||
```csharp
|
||||
private const int num = 1729;
|
||||
public const string title = "Grand" + " Master";
|
||||
```
|
||||
|
||||
The `readonly` modifier can be (and generally should be) applied to any field that cannot be made `const` where its value will not change during the lifetime of the program and is either set by an inline initializer or during instantiation (by the constructor or a method called by the constructor).
|
||||
|
||||
```csharp
|
||||
private readonly int num;
|
||||
private readonly System.Random rand = new System.Random();
|
||||
|
||||
public MyClass(int num)
|
||||
{
|
||||
this.num = num;
|
||||
}
|
||||
```
|
||||
|
||||
## Readonly Collections
|
||||
|
||||
While the `readonly` modifier prevents the value or reference in a field from being overwritten, it offers no protection for the members of a reference type.
|
||||
|
||||
```csharp
|
||||
readonly List<int> ints = new List<int>();
|
||||
|
||||
void Foo()
|
||||
{
|
||||
ints.Add(1); // ok
|
||||
ints = new List<int>(); // fails to compile
|
||||
}
|
||||
```
|
||||
|
||||
To ensure that all members of a reference type are protected the fields can be made `readonly` and automatic properties can be defined without a `set` accessor.
|
||||
|
||||
The Base Class Library (BCL) provides some readonly versions of collections where there is a requirement to stop members of a collections being updated. These come in the form of wrappers:
|
||||
|
||||
- `ReadOnlyDictionary<T>` exposes a `Dictionary<T>` as read-only.
|
||||
- `ReadOnlyCollection<T>` exposes a `List<T>` as read-only.
|
||||
|
||||
## Defensive Copying
|
||||
|
||||
In security sensitive situations (or even simply on a large code-base where developers have different priorities and agendas) you should avoid allowing a class's public API to be circumvented by accepting and storing a method's mutable parameters or by exposing a mutable member of a class through a return value or as an `out` parameter.
|
||||
|
||||
## Instructions
|
||||
|
||||
The authentication system that you last saw in [exercise:csharp/developer-privileges]() is in need of some attention. You have been tasked with cleaning up the code. Such a cleanup project will not only make life easy for future maintainers but will expose and fix some security vulnerabilities.
|
||||
|
||||
## 1. Set appropriate fields and properties to const
|
||||
|
||||
This is a refactoring task. Add the `const` modifier to any members of `Authenticator` or `Identity` that you think appropriate.
|
||||
|
||||
## 2. Set appropriate fields to readonly
|
||||
|
||||
This is a refactoring task. Add the `readonly` modifier to any fields of the `Authenticator` class or the `Identity` struct that you think appropriate.
|
||||
|
||||
## 3. Ensure that the class cannot be changed once it has been created
|
||||
|
||||
Remove the `set` accessor or make it `private` for any appropriate property on the `Authenticator` class or `Identity` struct.
|
||||
|
||||
## 4. Ensure that the admin cannot be tampered with
|
||||
|
||||
At present the admin identity field is returned by a call to `Admin`. This is not ideal as the caller can modify the field. Find a way to prevent the caller modifying the details of admin on the `Authenticator` object.
|
||||
|
||||
## 5. Ensure that the developers cannot be tampered with
|
||||
|
||||
At present the dictionary containing the hard coded privileged developer identities is returned by a call to `GetDevelopers()`. This is not ideal as the caller can modify the dictionary. Find a way to prevent the caller modifying the details of admin on the `Authenticator` object.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @mikedamay
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @ErikSchierboom
|
||||
- @yzAlvin
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"blurb": "Learn about dictionaries by keeping track of international dialling codes.",
|
||||
"contributors": [
|
||||
"ErikSchierboom",
|
||||
"valentin-p",
|
||||
"yzAlvin"
|
||||
],
|
||||
"authors": [
|
||||
"mikedamay"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"InternationalCallingConnoisseur.cs"
|
||||
],
|
||||
"test": [
|
||||
"InternationalCallingConnoisseurTests.cs"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/Exemplar.cs"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"track":"csharp","exercise":"international-calling-connoisseur","id":"03a5da35918c4defa48b9db20aa17c3f","url":"https://exercism.org/tracks/csharp/exercises/international-calling-connoisseur","handle":"benharri","is_requester":true,"auto_approve":false}
|
|
@ -0,0 +1,39 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
You can run the tests by opening a command prompt in the exercise's directory, and then running the [`dotnet test` command](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test)
|
||||
Alternatively, most IDE's have built-in support for running tests, including [Visual Studio](https://docs.microsoft.com/en-us/visualstudio/test/run-unit-tests-with-test-explorer), [Rider](https://www.jetbrains.com/help/rider/Unit_Testing_in_Solution.html) and [Visual Studio code](https://github.com/OmniSharp/omnisharp-vscode/wiki/How-to-run-and-debug-unit-tests).
|
||||
See the [tests page](https://exercism.io/tracks/csharp/tests) for more information.
|
||||
|
||||
## Skipped tests
|
||||
|
||||
Initially, only the first test will be enabled.
|
||||
This is to encourage you to solve the exercise one step at a time.
|
||||
Once you get the first test passing, remove the `Skip` property from the next test and work on getting that test passing.
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit InternationalCallingConnoisseur.cs` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [C# track's documentation](https://exercism.org/docs/tracks/csharp)
|
||||
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
To get help if you're having trouble, you can use one of the following resources:
|
||||
|
||||
- [Gitter](https://gitter.im/exercism/xcsharp) is Exercism C# track's Gitter room; go here to get support and ask questions related to the C# track.
|
||||
- [/r/csharp](https://www.reddit.com/r/csharp) is the C# subreddit.
|
||||
- [StackOverflow](http://stackoverflow.com/questions/tagged/c%23) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
|
|
@ -0,0 +1,56 @@
|
|||
# Hints
|
||||
|
||||
## 1. Create a new dictionary
|
||||
|
||||
A dictionary is like any other class. You simply 'new' it to create an empty instance.
|
||||
|
||||
## 2. Create a pre-populated dictionary
|
||||
|
||||
Although it's possible to populate a dictionary by repeatedly adding items, dictionaries can be initialized statically.
|
||||
|
||||
See [this article][dictionary_static_initialization].
|
||||
|
||||
## 3. Add a country to an empty dictionary
|
||||
|
||||
See [Add][dictionary_add]. Pass in the dictionary returned by task 1 as a parameter.
|
||||
|
||||
## 4. Add a country to an existing dictionary
|
||||
|
||||
There is no substantial difference between adding an item to an empty or initialized dictionary. Pass in the dictionary returned by task 2 as a parameter.
|
||||
|
||||
## 5. Get the country name matching a country Code
|
||||
|
||||
See [this article][dictionary_item].
|
||||
|
||||
## 6. Attempt to get country name for a non-existent country code
|
||||
|
||||
You need to [detect][dictionary_contains_key] whether the country is present in the dictionary.
|
||||
|
||||
## 7. Attempt to get country name for a non-existent country code
|
||||
|
||||
You can combine what you've learnt in Tasks 5 and 6 to solve this one.
|
||||
|
||||
## 8. Update a country name
|
||||
|
||||
Again [this article][dictionary_item] applies.
|
||||
|
||||
## 9. Attempt to ypdate name of country that is not in the dictionary
|
||||
|
||||
This is very similar to task 7.
|
||||
|
||||
## 10. Remove a country from the dictionary
|
||||
|
||||
See [this article][dictionary_remove].
|
||||
|
||||
## 11. Find the country with the longest name
|
||||
|
||||
See the [values collection][dictionary_values], [string length][string_length] and [foreach][foreach].
|
||||
|
||||
[dictionary_static_initialization]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/how-to-initialize-a-dictionary-with-a-collection-initializer
|
||||
[dictionary_add]: https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.add?view=netcore-3.1
|
||||
[dictionary_item]: https://docs.microsoft.com/en-gb/dotnet/api/system.collections.generic.dictionary-2.item?view=netcore-3.1
|
||||
[dictionary_contains_key]: https://docs.microsoft.com/en-gb/dotnet/api/system.collections.generic.dictionary-2.containskey?view=netcore-3.1
|
||||
[dictionary_remove]: https://docs.microsoft.com/en-gb/dotnet/api/system.collections.generic.dictionary-2.remove?view=netcore-3.1
|
||||
[dictionary_values]: https://docs.microsoft.com/en-gb/dotnet/api/system.collections.generic.dictionary-2.values?view=netcore-3.1
|
||||
[foreach]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/foreach-in
|
||||
[string_length]: https://docs.microsoft.com/en-gb/dotnet/api/system.string.length?view=netcore-3.1#System_String_Length
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public static class DialingCodes
|
||||
{
|
||||
public static Dictionary<int, string> GetEmptyDictionary() => new();
|
||||
|
||||
public static Dictionary<int, string> GetExistingDictionary() =>
|
||||
new()
|
||||
{
|
||||
{ 1, "United States of America" },
|
||||
{ 55, "Brazil" },
|
||||
{ 91, "India" }
|
||||
};
|
||||
|
||||
public static Dictionary<int, string> AddCountryToEmptyDictionary(int countryCode, string countryName) =>
|
||||
new()
|
||||
{
|
||||
{ countryCode, countryName }
|
||||
};
|
||||
|
||||
public static Dictionary<int, string> AddCountryToExistingDictionary(
|
||||
Dictionary<int, string> existingDictionary, int countryCode, string CountryName)
|
||||
{
|
||||
existingDictionary.Add(countryCode, CountryName);
|
||||
return existingDictionary;
|
||||
}
|
||||
|
||||
public static string GetCountryNameFromDictionary(
|
||||
Dictionary<int, string> existingDictionary, int countryCode) =>
|
||||
existingDictionary.GetValueOrDefault(countryCode, string.Empty);
|
||||
|
||||
public static Dictionary<int, string> UpdateDictionary(
|
||||
Dictionary<int, string> existingDictionary, int countryCode, string countryName)
|
||||
{
|
||||
if (existingDictionary.ContainsKey(countryCode))
|
||||
existingDictionary[countryCode] = countryName;
|
||||
return existingDictionary;
|
||||
}
|
||||
|
||||
public static Dictionary<int, string> RemoveCountryFromDictionary(
|
||||
Dictionary<int, string> existingDictionary, int countryCode)
|
||||
{
|
||||
existingDictionary.Remove(countryCode);
|
||||
return existingDictionary;
|
||||
}
|
||||
|
||||
public static bool CheckCodeExists(Dictionary<int, string> existingDictionary, int countryCode) =>
|
||||
existingDictionary.ContainsKey(countryCode);
|
||||
|
||||
public static string FindLongestCountryName(Dictionary<int, string> existingDictionary) =>
|
||||
existingDictionary.Values.OrderByDescending(n => n.Length).FirstOrDefault() ?? string.Empty;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="Exercism.Tests" Version="0.1.0-beta1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,295 @@
|
|||
using Xunit;
|
||||
using Exercism.Tests;
|
||||
|
||||
public class InternationalCallingConnoisseurTests
|
||||
{
|
||||
[Fact]
|
||||
[Task(1)]
|
||||
public void Empty_dictionary_is_empty()
|
||||
{
|
||||
var emptyDict = DialingCodes.GetEmptyDictionary();
|
||||
Assert.Empty(emptyDict);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(2)]
|
||||
public void Existing_dictionary_count_is_3()
|
||||
{
|
||||
var prePopulated = DialingCodes.GetExistingDictionary();
|
||||
Assert.Equal(3, prePopulated.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(2)]
|
||||
public void Existing_dictionary_1_is_United_States_of_America()
|
||||
{
|
||||
var prePopulated = DialingCodes.GetExistingDictionary();
|
||||
Assert.Equal("United States of America", prePopulated[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(2)]
|
||||
public void Existing_dictionary_55_is_Brazil()
|
||||
{
|
||||
var prePopulated = DialingCodes.GetExistingDictionary();
|
||||
Assert.Equal("Brazil", prePopulated[55]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(2)]
|
||||
public void Existing_dictionary_91_is_India()
|
||||
{
|
||||
var prePopulated = DialingCodes.GetExistingDictionary();
|
||||
Assert.Equal("India", prePopulated[91]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(3)]
|
||||
public void Add_country_to_empty_dictionary_single()
|
||||
{
|
||||
var countryCodes = DialingCodes.AddCountryToEmptyDictionary(44, "United Kingdom");
|
||||
Assert.Single(countryCodes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(3)]
|
||||
public void Add_country_to_empty_dictionary_44_is_United_Kingdom()
|
||||
{
|
||||
var countryCodes = DialingCodes.AddCountryToEmptyDictionary(44, "United Kingdom");
|
||||
Assert.Equal("United Kingdom", countryCodes[44]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(4)]
|
||||
public void Add_country_to_existing_dictionary_count_is_1()
|
||||
{
|
||||
var countryCodes = DialingCodes.AddCountryToExistingDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 44, "United Kingdom");
|
||||
Assert.Equal(4, countryCodes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(4)]
|
||||
public void Add_country_to_existing_dictionary_1_is_United_States_of_America()
|
||||
{
|
||||
var countryCodes = DialingCodes.AddCountryToExistingDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 44, "United Kingdom");
|
||||
Assert.Equal("United States of America", countryCodes[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(4)]
|
||||
public void Add_country_to_existing_dictionary_44_is_United_Kingdom()
|
||||
{
|
||||
var countryCodes = DialingCodes.AddCountryToExistingDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 44, "United Kingdom");
|
||||
Assert.Equal("United Kingdom", countryCodes[44]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(4)]
|
||||
public void Add_country_to_existing_dictionary_55_is_Brazil()
|
||||
{
|
||||
var countryCodes = DialingCodes.AddCountryToExistingDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 44, "United Kingdom");
|
||||
Assert.Equal("Brazil", countryCodes[55]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(4)]
|
||||
public void Add_country_to_existing_dictionary_91_is_India()
|
||||
{
|
||||
var countryCodes = DialingCodes.AddCountryToExistingDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 44, "United Kingdom");
|
||||
Assert.Equal("India", countryCodes[91]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(5)]
|
||||
public void Get_country_name_from_dictionary()
|
||||
{
|
||||
var countryName = DialingCodes.GetCountryNameFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 55);
|
||||
Assert.Equal("Brazil", countryName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(5)]
|
||||
public void Get_country_name_for_non_existent_country()
|
||||
{
|
||||
var countryName = DialingCodes.GetCountryNameFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999);
|
||||
Assert.Equal(string.Empty, countryName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(6)]
|
||||
public void Check_country_exists()
|
||||
{
|
||||
var exists = DialingCodes.CheckCodeExists(
|
||||
DialingCodes.GetExistingDictionary(), 55);
|
||||
Assert.True(exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(6)]
|
||||
public void Check_country_exists_for_non_existent_country()
|
||||
{
|
||||
var exists = DialingCodes.CheckCodeExists(
|
||||
DialingCodes.GetExistingDictionary(), 999);
|
||||
Assert.False(exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(7)]
|
||||
public void Update_country_name_in_dictionary_count_is_3()
|
||||
{
|
||||
var countryCodes = DialingCodes.UpdateDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 1, "les États-Unis");
|
||||
Assert.Equal(3, countryCodes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(7)]
|
||||
public void Update_country_name_in_dictionary_1_is_les_Etats_Unis()
|
||||
{
|
||||
var countryCodes = DialingCodes.UpdateDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 1, "les États-Unis");
|
||||
Assert.Equal("les États-Unis", countryCodes[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(7)]
|
||||
public void Update_country_name_in_dictionary_55_is_Brazil()
|
||||
{
|
||||
var countryCodes = DialingCodes.UpdateDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 1, "les États-Unis");
|
||||
Assert.Equal("Brazil", countryCodes[55]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(7)]
|
||||
public void Update_country_name_in_dictionary_91_is_India()
|
||||
{
|
||||
var countryCodes = DialingCodes.UpdateDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 1, "les États-Unis");
|
||||
Assert.Equal("India", countryCodes[91]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(7)]
|
||||
public void Update_country_name_in_dictionary_for_non_existent_country_count_is_3()
|
||||
{
|
||||
var countryCodes = DialingCodes.UpdateDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999, "Newlands");
|
||||
Assert.Equal(3, countryCodes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(7)]
|
||||
public void Update_country_name_in_dictionary_for_non_existent_country_1_is_United_States_of_America()
|
||||
{
|
||||
var countryCodes = DialingCodes.UpdateDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999, "Newlands");
|
||||
Assert.Equal("United States of America", countryCodes[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(7)]
|
||||
public void Update_country_name_in_dictionary_for_non_existent_country_55_is_Brazil()
|
||||
{
|
||||
var countryCodes = DialingCodes.UpdateDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999, "Newlands");
|
||||
Assert.Equal("Brazil", countryCodes[55]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(7)]
|
||||
public void Update_country_name_in_dictionary_for_non_existent_country_91_is_India()
|
||||
{
|
||||
var countryCodes = DialingCodes.UpdateDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999, "Newlands");
|
||||
Assert.Equal("India", countryCodes[91]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(8)]
|
||||
public void Remove_country_from_dictionary_count_is_2()
|
||||
{
|
||||
var countryCodes = DialingCodes.RemoveCountryFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 91);
|
||||
Assert.Equal(2, countryCodes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(8)]
|
||||
public void Remove_country_from_dictionary_1_is_United_States_of_America()
|
||||
{
|
||||
var countryCodes = DialingCodes.RemoveCountryFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 91);
|
||||
Assert.Equal("United States of America", countryCodes[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(8)]
|
||||
public void Remove_country_from_dictionary_55_is_Brazil()
|
||||
{
|
||||
var countryCodes = DialingCodes.RemoveCountryFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 91);
|
||||
Assert.Equal("Brazil", countryCodes[55]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(8)]
|
||||
public void Remove_country_from_dictionary_for_non_existent_country_count_is_3()
|
||||
{
|
||||
var countryCodes = DialingCodes.RemoveCountryFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999);
|
||||
Assert.Equal(3, countryCodes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(8)]
|
||||
public void Remove_country_from_dictionary_for_non_existent_country_1_is_United_States_of_America()
|
||||
{
|
||||
var countryCodes = DialingCodes.RemoveCountryFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999);
|
||||
Assert.Equal("United States of America", countryCodes[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(8)]
|
||||
public void Remove_country_from_dictionary_for_non_existent_country_55_is_Brazil()
|
||||
{
|
||||
var countryCodes = DialingCodes.RemoveCountryFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999);
|
||||
Assert.Equal("Brazil", countryCodes[55]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(8)]
|
||||
public void Remove_country_from_dictionary_for_non_existent_country_91_is_India()
|
||||
{
|
||||
var countryCodes = DialingCodes.RemoveCountryFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999);
|
||||
Assert.Equal("India", countryCodes[91]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(9)]
|
||||
public void Longest_country_name()
|
||||
{
|
||||
var longestCountryName = DialingCodes.FindLongestCountryName(
|
||||
DialingCodes.GetExistingDictionary());
|
||||
Assert.Equal("United States of America", longestCountryName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Task(9)]
|
||||
public void Longest_country_name_for_empty_dictionary()
|
||||
{
|
||||
var longestCountryName = DialingCodes.FindLongestCountryName(
|
||||
DialingCodes.GetEmptyDictionary());
|
||||
Assert.Equal(string.Empty, longestCountryName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
# International Calling Connoisseur
|
||||
|
||||
Welcome to International Calling Connoisseur on Exercism's C# Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
A dictionary is a collection of elements where each element comprises a key and value such that if a key is passed to a method of the dictionary its associated value is returned. It has the same role as maps or associative arrays do in other languages.
|
||||
|
||||
A dictionary can be created as follows:
|
||||
|
||||
```csharp
|
||||
new Dictionary<int, string>();
|
||||
// Empty dictionary
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
```csharp
|
||||
new Dictionary<int, string>
|
||||
{
|
||||
[1] = "One",
|
||||
[2] = "Two"
|
||||
};
|
||||
|
||||
// Or
|
||||
|
||||
new Dictionary<int, string>
|
||||
{
|
||||
{1, "One"},
|
||||
{2, "Two"}
|
||||
};
|
||||
// 1 => "One", 2 => "Two"
|
||||
```
|
||||
|
||||
Note that the key and value types are part of the definition of the dictionary.
|
||||
|
||||
Once constructed, entries can be added or removed from a dictionary using its built-in methods `Add` and `Remove`.
|
||||
|
||||
Retrieving or updating values in a dictionary is done by indexing into the dictionary using a key:
|
||||
|
||||
```csharp
|
||||
var numbers = new Dictionary<int, string>
|
||||
{
|
||||
{1, "One"},
|
||||
{2, "Two"}
|
||||
};
|
||||
|
||||
// Set the value of the element with key 2 to "Deux"
|
||||
numbers[2] = "Deux";
|
||||
|
||||
// Get the value of the element with key 2
|
||||
numbers[2];
|
||||
// => "Deux"
|
||||
```
|
||||
|
||||
You can test if a value exists in the dictionary with:
|
||||
|
||||
```csharp
|
||||
var dict = new Dictionary<string, string>{/*...*/};
|
||||
dict.ContainsKey("some key that exists");
|
||||
// => true
|
||||
```
|
||||
|
||||
Enumerating over a dictionary will enumerate over its key/value pairs. Dictionaries also have properties that allow enumerating over its keys or values.
|
||||
|
||||
[indexer-properties]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/
|
||||
|
||||
## Instructions
|
||||
|
||||
In this exercise you'll be writing code to keep track of international dialling codes via an international dialing code dictionary.
|
||||
|
||||
The dictionary uses an integer for its keys (the dialing code) and a string (country name) for its values.
|
||||
|
||||
You have 9 tasks which involve the `DialingCodes` static class.
|
||||
|
||||
## 1. Create a new dictionary
|
||||
|
||||
Implement the (static) method `DialingCodes.GetEmptyDictionary()` that returns an empty dictionary.
|
||||
|
||||
```csharp
|
||||
DialingCodes.GetEmptyDictionary();
|
||||
// => empty dictionary
|
||||
```
|
||||
|
||||
## 2. Create a pre-populated dictionary
|
||||
|
||||
There exists a pre-populated dictionary which contains the following 3 dialing codes: "United States of America" which has a code of 1, "Brazil" which has a code of 55 and "India" which has a code of 91. Implement the (static) `DialingCodes.GetExistingDictionary()` method to return the pre-populated dictionary:
|
||||
|
||||
```csharp
|
||||
DialingCodes.GetExistingDictionary();
|
||||
// => 1 => "United States of America", 55 => "Brazil", 91 => "India"
|
||||
```
|
||||
|
||||
## 3. Add a country to an empty dictionary
|
||||
|
||||
Implement the (static) method `DialingCodes.AddCountryToEmptyDictionary()` that creates a dictionary and adds a dialing code and associated country name to it.
|
||||
|
||||
```csharp
|
||||
DialingCodes.AddCountryToEmptyDictionary(44, "United Kingdom");
|
||||
// => 44 => "United Kingdom"
|
||||
```
|
||||
|
||||
## 4. Add a country to an existing dictionary
|
||||
|
||||
Implement the (static) method `DialingCodes.AddCountryToExistingDictionary()` that adds a dialing code and associated country name to a non-empty dictionary.
|
||||
|
||||
```csharp
|
||||
DialingCodes.AddCountryToExistingDictionary(DialingCodes.GetExistingDictionary(),
|
||||
44, "United Kingdom");
|
||||
// => 1 => "United States of America", 44 => "United Kingdom", 55 => "Brazil", 91 => "India"
|
||||
```
|
||||
|
||||
## 5. Get the country name matching a dialing code
|
||||
|
||||
Implement the (static) method `DialingCodes.GetCountryNameFromDictionary()` that takes a dialing code and returns the corresponding country name. If the dialing code is not contained in the dictionary then an empty string is returned.
|
||||
|
||||
```csharp
|
||||
DialingCodes.GetCountryNameFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 55);
|
||||
// => "Brazil"
|
||||
|
||||
DialingCodes.GetCountryNameFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999);
|
||||
// => string.Empty
|
||||
```
|
||||
|
||||
## 6. Check that a country exists in the dictionary
|
||||
|
||||
Implement the (static) method `DialingCodes.CheckCodeExists()` to check whether a dialing code exists in the dictionary.
|
||||
|
||||
```csharp
|
||||
DialingCodes.CheckCodeExists(DialingCodes.GetExistingDictionary(), 55);
|
||||
// => true
|
||||
```
|
||||
|
||||
## 7. Update a country name
|
||||
|
||||
Implement the (static) method `DialingCodes.UpdateDictionary()` which takes a dialing code and replaces the corresponding country name in the dictionary with the country name passed as a parameter. If the dialing code does not exist in the dictionary then the dictionary remains unchanged.
|
||||
|
||||
```csharp
|
||||
DialingCodes.UpdateDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 1, "Les États-Unis");
|
||||
// => 1 => "Les États-Unis", 55 => "Brazil", 91 => "India"
|
||||
|
||||
DialingCodes.UpdateDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 999, "Newlands");
|
||||
// 1 => "United States of America", 55 => "Brazil", 91 => "India"
|
||||
```
|
||||
|
||||
## 8. Remove a country from the dictionary
|
||||
|
||||
Implement the (static) method `DialingCodes.RemoveCountryFromDictionary()` that takes a dialing code and will remove the corresponding record, dialing code + country name, from the dictionary.
|
||||
|
||||
```csharp
|
||||
DialingCodes.RemoveCountryFromDictionary(
|
||||
DialingCodes.GetExistingDictionary(), 91);
|
||||
// => 1 => "United States of America", 55 => "Brazil"
|
||||
```
|
||||
|
||||
## 9. Find the country with the longest name
|
||||
|
||||
Implement the (static) method `DialingCodes.FindLongestCountryName()` which will return the name of the country with the longest name stored in the dictionary.
|
||||
|
||||
```csharp
|
||||
DialingCodes.FindLongestCountryName(
|
||||
DialingCodes.GetExistingDictionary());
|
||||
// => "United States of America"
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @mikedamay
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @ErikSchierboom
|
||||
- @valentin-p
|
||||
- @yzAlvin
|
Loading…
Reference in New Issue