Flexible Coding
148 subscribers
163 photos
2 files
96 links
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет!
Начал писать цикл статей про многопоточность на разных уровнях абстракции. И представляю вам первую статью из этой серии - про процессор!
Интеграционное тестирование MongoDB. Точки отказа

Всем привет! Сегодня рассмотрим тему настроек MongoDB для интгерационного тестирования и узнаем, что такое FailPoint и как он может помочь проверить нашу логику в нестандартных ситуациях.

Часто нам приходится писать интеграционные тесты. Интеграционные тесты — это тесты, которые проверяют взаимодействие нашего кода с внешними системами (БД, внешние API и т. д.) в как можно более приближённых к реальности условиях.

Зачем нужны такие тесты? Ну, например, чтобы убедиться, что наша логика корректно обрабатывает ситуации, когда база данных возвращает ошибку создания индекса или отказывает в записи. Юнит-тесты здесь уже не справятся, ведь они не покрывают фактическое взаимодействие с “живой” MongoDB.

Test Commands в MongoDB
MongoDB поддерживает набор специальных команд для тестирования (testCommands), которые обычно отключены в продакшене. Среди них есть конфигурация FailPoint — «точка отказа», позволяющая искусственно вызывать сбои и ошибки в различных операциях.

Чтобы включить testCommands, достаточно при запуске MongoDB задать параметр enableTestCommands=1. Вот пример docker-compose для этой задачи:


services:
mongo:
image: mongo:6.0
command: mongod --setParameter enableTestCommands=1
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: password


Для запуска команд нужно выбрать базу данных (для ConfigureFailPoint это база данных admin) и вызвать метод db.runCommand({}).

Определение ConfigureFailPoint состоит из следующих полей:
1. configureFailPoint - название точки отказа, которую мы настраиваем. Чаще всего встречается значение failCommand для настройки отказа конкретной команды
2. mode: определяет режим срабатывания точки отказа: alwaysOn, off, {times: n} и другие
3. data - является вложенным объектом, в котором указываются детали и условия сбоя

Пример использования для команды createIndexes:


use admin;
db.runCommand({
configureFailPoint: "failCommand",
mode: { times: 1 },
data: {
failCommands: ["createIndexes"],
errorCode: 100,
closeConnection: false
}
});


А когда тесты будут завершены, точку отказа можно отключить:


use admin;
db.runCommand({
configureFailPoint: "failCommand",
mode: "off"
});


В следующем посте мы рассмотрим пример интеграционного теста, который использует данную функциональность, а пока что можете погрузиться в вики-страничку про данный механизм.

Всем спасибо, с вами был Flexible Coding!
Интеграционное тестирование MongoDB. Точки отказа. .NET

Всем привет! Продолжаем тему точек отказа у MongoDb. С основами разобрались, а теперь приступим к написанию тестов на .NET.
Тесты разделяются на три этапа: Given (Arrange), When (Act) и Then (Assert). Так же в рамках теста может происходит подготовка окружения - Setup и очистка - TearDown.

В рамках конкретного теста точку отказа удобнее всего настраивать на этапе Setup. В NUnit это будет выглядеть следующим образом:

[SetUp] 
public void Setup() 

    // Подключаемся к локальной монге, где включены testCommands 
    var client = new MongoClient("mongodb://root:password@localhost:27017"); 
    _database = client.GetDatabase("testDb"); 
    _collection = _database.GetCollection<BsonDocument>("testCollection"); 
    _adminDatabase = client.GetDatabase("admin"); 
 
    // Включаем FailPoint для команды createIndexes 
    var enableFailPoint = new BsonDocument 
    { 
        { "configureFailPoint", "failCommand" }, 
        { "mode", new BsonDocument("times", 1) }, 
        { 
            "data", new BsonDocument 
            { 
                { "failCommands", new BsonArray { "createIndexes" } }, 
                { "errorCode", 100 }, // код ошибки (для проверки) 
                { "closeConnection", false } // соединение не рвём 
            } 
        } 
    }; 
 
    _adminDatabase.RunCommand<BsonDocument>(enableFailPoint); 
}


Далее - сама логика теста:

[Test] 
public void CreateIndexes_ShouldThrowException_WhenFailPointEnabled() 

    var ex = Assert.Throws<MongoCommandException>(() => 
    { 
        _collection.Indexes.CreateOne( 
            new CreateIndexModel<BsonDocument>( 
                Builders<BsonDocument>.IndexKeys.Ascending("someField"))); 
    }); 
 
    Assert.That(ex.Code, Is.EqualTo(100)); 
}


А затем возвращаем MongoDB в исходное состояние:

[TearDown] 
public void TearDown() 

    // Выключаем FailPoint, чтобы вернуть MongoDb в исходное состояние 
    var disableFailPoint = new BsonDocument 
    { 
        { "configureFailPoint", "failCommand" }, 
        { "mode", "off" } 
    }; 
 
    _adminDatabase.RunCommand<BsonDocument>(disableFailPoint); 
}


Таким образом можно настраивать необходимое поведение БД в рамках теста.

Flexible Coding
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
var functions = new List<Func<int, int>>();

for (int i = 0; i < 10; i++)
{
Func<int, int> function = x => i * x;
if (!functions.Contains(function))
{
functions.Add(function);
}
}

Console.WriteLine(functions.Count);
Правильный ответ - 1. Почему так? Давайте разбираться

Сравнение делегатов

Сперва нам кажется, что тут всё просто. Func<int, int> function = x => i * x; - это создание новой переменной. На основе документации Microsoft узнаём, что делегат - это reference type, значит они сравниваются по ссылке. А раз мы на каждой итерации цикла создаём новую переменную function - у неё каждый раз будет новый адрес в памяти и Contains всегда вернёт false.

Но делегат - это непростой тип в .NET. Давайте посмотрим, из каких полей он состоит.

Method
Это поле с типом данных MethodInfo, который должен быть вызван при обращении к делегату. В памяти он представлен через внутренние поля _methodPtr и _methodPtrAux, которые являются указателями на конкретную функцию.

Target
Это объект, к которому привязан метод, если он нестатический. То есть если метод — экземплярный, Target указывает на объект, для которого этот метод будет вызван. Если метод статический, Target может быть null.

У делегатов так же переопределён метод Equals. В репозитории reference source можно увидеть полную его реализацию, и нас интересует следующий фрагмент:


// do an optimistic check first. This is hopefully cheap enough to be worth
if (_target == d._target && _methodPtr == d._methodPtr && _methodPtrAux == d._methodPtrAux)
return true;


Приватные поля _target, _methodPtr и _methodPtrAux могут нам указывать на то, что два делегата равны. Они как раз дают нам информацию о методе и привязке метода к объекту. Попробуем вытащить их из делегата (тут нам пригодится немного магии unsafe):


void PrintFunctionInfo(Func<int, int> function)
{
unsafe
{
TypedReference tr = __makeref(function);
IntPtr* ptr = (IntPtr*)*(IntPtr*)&tr;

Console.WriteLine($"Pointer to Delegate: 0x{(ulong)ptr:X}");

IntPtr target = ptr[1];
IntPtr methodPtr = ptr[3];
IntPtr methodPtrAux = ptr[4];

Console.WriteLine($"Target: 0x{(ulong)target:X}");
Console.WriteLine($"MethodPtr: 0x{(ulong)methodPtr:X}");
Console.WriteLine($"MethodPtrAux: 0x{(ulong)methodPtrAux:X}");
}
}

Если мы вызовем этот метод в цикле из примера выше, то обнаружим одинаковые адреса делегата на каждой итерации цикла.

Как вы думаете, почему так?

Flexible Coding
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Всем привет! Вот и наступило лето, а у нас - итоги прошедшего сезона!

Этой весной мы:

💥 Контролируемо ломали MongoDB - первая и вторая части

😤 Расстраивались из-за Open Source, который становится всё дороже

🧠 Закончили цикл статей про многопоточность (или нет???) - 2, 3 и 4

🔀 Сравнивали делегаты

Пропускали точку с запятой в SQL

🤖 А так же смотрели на ИИ и рисовали схемки в Excalidraw

Flexible Coding