authentication-system

This commit is contained in:
Ben Harris 2021-11-08 15:32:45 -05:00
parent 82471ecd52
commit b0b200cb0a
16 changed files with 938 additions and 0 deletions

View File

@ -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"
]
}
}

View File

@ -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}

View File

@ -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; }
}

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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"
]
}
}

View File

@ -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}

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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