1259 lines
56 KiB
C#
1259 lines
56 KiB
C#
#nullable enable
|
|
|
|
using Cysharp.Threading.Tasks;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using UnityEngine;
|
|
using UVC.Data;
|
|
using UVC.Data.Core;
|
|
using UVC.Data.Http;
|
|
|
|
namespace UVC.Sample.Data
|
|
{
|
|
/// <summary>
|
|
/// 샘플 코드입니다. DataSampleRunner Inspector에서 실행하거나 Play 버튼으로 확인합니다.
|
|
/// HttpDataFetcher 클래스의 테스트를 위한 테스트 클래스입니다.
|
|
/// </summary>
|
|
public class HttpDataFetcherTests
|
|
{
|
|
// 테스트에 사용할 HttpDataFetcher 인스턴스
|
|
private HttpDataFetcher? pipeLine;
|
|
|
|
/// <summary>
|
|
/// 각 테스트 실행 전에 호출되는 설정 메서드입니다.
|
|
/// </summary>
|
|
public void Setup()
|
|
{
|
|
pipeLine = new HttpDataFetcher();
|
|
pipeLine.UseMockup = true; // MockHttpRequester 사용 설정
|
|
// 테스트를 위한 DataRepository 초기화
|
|
ClearDataRepository();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 모든 테스트 메서드를 실행하는 메서드입니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 이 메서드는 클래스의 모든 테스트 메서드를 순차적으로 호출하고
|
|
/// 각 테스트의 성공 또는 실패 여부를 로그로 출력합니다.
|
|
/// </remarks>
|
|
public async UniTask RunAll()
|
|
{
|
|
Setup();
|
|
|
|
Debug.Log("===== HttpDataFetcher 테스트 시작 =====");
|
|
//RunTest(nameof(Add_NewInfo_AddedSuccessfully), Add_NewInfo_AddedSuccessfully);
|
|
//RunTest(nameof(Add_ExistingInfo_UpdatesExistingEntry), Add_ExistingInfo_UpdatesExistingEntry);
|
|
//await RunTestAsync(nameof(Remove_ExistingInfo_RemovedSuccessfullyAsync), Remove_ExistingInfo_RemovedSuccessfullyAsync);
|
|
//await RunTestAsync(nameof(Remove_NonExistingInfo_DoesNothing), Remove_NonExistingInfo_DoesNothing);
|
|
//RunTest(nameof(Excute_WithNonExistingKey_DoesNothing), Excute_WithNonExistingKey_DoesNothing);
|
|
//await RunTestAsync(nameof(Excute_WithJObjectResponse_ProcessesDataCorrectly), Excute_WithJObjectResponse_ProcessesDataCorrectly);
|
|
//await RunTestAsync(nameof(Excute_WithJArrayResponse_ProcessesDataCorrectly), Excute_WithJArrayResponse_ProcessesDataCorrectly);
|
|
//await RunTestAsync(nameof(Test_Excute_AgvDataParsing), Test_Excute_AgvDataParsing);
|
|
//await RunTestAsync(nameof(Test_Excute_AlarmDataParsing), Test_Excute_AlarmDataParsing);
|
|
//await RunTestAsync(nameof(Test_Excute_MultipleDataTypes), Test_Excute_MultipleDataTypes);
|
|
//await RunTestAsync(nameof(Test_Excute_CarrierDataParsing), Test_Excute_CarrierDataParsing);
|
|
//await RunTestAsync(nameof(Test_Excute_BaseInfoDataParsing), Test_Excute_BaseInfoDataParsing);
|
|
//await RunTestAsync(nameof(Test_Excute_WithRepeatExecution), Test_Excute_WithRepeatExecution);
|
|
//await RunTestAsync(nameof(Test_StopRepeat_StopsExecutionCorrectly), Test_StopRepeat_StopsExecutionCorrectly);
|
|
//await RunTestAsync(nameof(Test_MultipleRepeatingRequests_ManagedIndependently), Test_MultipleRepeatingRequests_ManagedIndependently);
|
|
//await RunTestAsync(nameof(Test_RepeatWithCount_StopsAutomatically), Test_RepeatWithCount_StopsAutomatically);
|
|
|
|
// HttpResponseMask 테스트 추가
|
|
//Debug.Log("===== HttpResponseMask 테스트 시작 =====");
|
|
//RunTest(nameof(HttpResponseMask_Apply_SuccessfulResponse_ReturnsSuccessWithData), HttpResponseMask_Apply_SuccessfulResponse_ReturnsSuccessWithData);
|
|
//RunTest(nameof(HttpResponseMask_Apply_FailedResponse_WrongSuccessValue_ReturnsFailWithMessage), HttpResponseMask_Apply_FailedResponse_WrongSuccessValue_ReturnsFailWithMessage);
|
|
//RunTest(nameof(HttpResponseMask_Apply_FailedResponse_MissingSuccessKey_ReturnsFailWithMessage), HttpResponseMask_Apply_FailedResponse_MissingSuccessKey_ReturnsFailWithMessage);
|
|
//RunTest(nameof(HttpResponseMask_Apply_FailedResponse_MissingDataKey_ReturnsFailWithMessage), HttpResponseMask_Apply_FailedResponse_MissingDataKey_ReturnsFailWithMessage);
|
|
//RunTest(nameof(HttpResponseMask_Apply_SuccessfulResponse_CustomKeys_ReturnsSuccessWithData), HttpResponseMask_Apply_SuccessfulResponse_CustomKeys_ReturnsSuccessWithData);
|
|
//RunTest(nameof(HttpResponseMask_Apply_InvalidJson_ThrowsException), HttpResponseMask_Apply_InvalidJson_ThrowsException);
|
|
//Debug.Log("===== HttpResponseMask 테스트 완료 =====");
|
|
|
|
Debug.Log("===== DataValidator 테스트 시작 =====");
|
|
await RunTestAsync(nameof(Test_Excute_WithValidData_ValidatorPasses), Test_Excute_WithValidData_ValidatorPasses);
|
|
await RunTestAsync(nameof(Test_Excute_WithInvalidData_ValidatorFails), Test_Excute_WithInvalidData_ValidatorFails);
|
|
await RunTestAsync(nameof(Test_Excute_WithArrayAndValidator_FiltersInvalidData), Test_Excute_WithArrayAndValidator_FiltersInvalidData);
|
|
Debug.Log("===== DataValidator 테스트 완료 =====");
|
|
|
|
Debug.Log("===== HttpDataFetcher 테스트 완료 =====");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 단일 테스트 메서드를 실행하고 결과를 로그로 출력합니다.
|
|
/// </summary>
|
|
/// <param name="testName">테스트 메서드 이름</param>
|
|
/// <param name="testAction">실행할 테스트 메서드</param>
|
|
private void RunTest(string testName, Action testAction)
|
|
{
|
|
try
|
|
{
|
|
Debug.Log($"테스트 시작: {testName}");
|
|
testAction();
|
|
Debug.Log($"테스트 성공: {testName}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"테스트 실패: {testName}\n{ex.Message}\n{ex.StackTrace}");
|
|
}
|
|
}
|
|
|
|
private async UniTask RunTestAsync(string testName, Func<UniTask> testAction)
|
|
{
|
|
try
|
|
{
|
|
Debug.Log($"테스트 시작: {testName}");
|
|
await testAction();
|
|
Debug.Log($"테스트 성공: {testName}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"테스트 실패: {testName}\n{ex.Message}\n{ex.StackTrace}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 검증 헬퍼 메서드입니다.
|
|
/// </summary>
|
|
private static void Check(bool condition, string message)
|
|
{
|
|
if (!condition) throw new Exception($"검증 실패: {message}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 새로운 HttpRequestConfig를 추가하는 테스트
|
|
/// </summary>
|
|
public void Add_NewInfo_AddedSuccessfully()
|
|
{
|
|
// Arrange
|
|
var info = new HttpRequestConfig("http://test.com");
|
|
|
|
// Act
|
|
pipeLine.Add("test", info);
|
|
|
|
// Assert - Dictionary에 추가되었는지 확인 (reflection 사용)
|
|
var infoList = GetInfoListField();
|
|
Check(infoList.ContainsKey("test"), "infoList.ContainsKey(\"test\")");
|
|
Check(info == infoList["test"], $"{infoList["test"]} != {info}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 기존에 존재하는 키로 HttpRequestConfig를 추가할 때 업데이트 테스트
|
|
/// </summary>
|
|
public void Add_ExistingInfo_UpdatesExistingEntry()
|
|
{
|
|
// Arrange
|
|
var info1 = new HttpRequestConfig("http://test1.com");
|
|
var info2 = new HttpRequestConfig("http://test2.com");
|
|
pipeLine.Add("test", info1);
|
|
|
|
// Act
|
|
pipeLine.Add("test", info2);
|
|
|
|
// Assert
|
|
var infoList = GetInfoListField();
|
|
Check(infoList.ContainsKey("test"), "infoList.ContainsKey(\"test\")");
|
|
Check(info2 == infoList["test"], $"{infoList["test"]} != {info2}");
|
|
Check(info1 != infoList["test"], $"info1 should not equal infoList[\"test\"]");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 존재하는 HttpRequestConfig를 제거하는 테스트
|
|
/// </summary>
|
|
public async UniTask Remove_ExistingInfo_RemovedSuccessfullyAsync()
|
|
{
|
|
// Arrange
|
|
var info = new HttpRequestConfig("http://test.com");
|
|
pipeLine.Add("test", info);
|
|
|
|
// Act
|
|
await pipeLine.RemoveAsync("test");
|
|
|
|
// Assert
|
|
var infoList = GetInfoListField();
|
|
Check(!infoList.ContainsKey("test"), "infoList.ContainsKey(\"test\")");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 존재하지 않는 키에 대한 RemoveChild 호출 테스트
|
|
/// </summary>
|
|
public async UniTask Remove_NonExistingInfo_DoesNothing()
|
|
{
|
|
// Arrange
|
|
var info = new HttpRequestConfig("http://test.com");
|
|
pipeLine.Add("test", info);
|
|
|
|
// Act - 존재하지 않는 키 제거 시도
|
|
await pipeLine.RemoveAsync("nonexistent");
|
|
|
|
// Assert - 기존 항목은 여전히 존재해야 함
|
|
var infoList = GetInfoListField();
|
|
Check(infoList.ContainsKey("test"), "infoList.ContainsKey(\"test\")");
|
|
Check(info == infoList["test"], $"{infoList["test"]} != {info}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// HttpDataFetcher의 private configList 필드 가져오기
|
|
/// </summary>
|
|
private Dictionary<string, HttpRequestConfig> GetInfoListField()
|
|
{
|
|
var fieldInfo = typeof(HttpDataFetcher).GetField("configList",
|
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
return (Dictionary<string, HttpRequestConfig>)fieldInfo.GetValue(pipeLine);
|
|
}
|
|
|
|
/// <summary>
|
|
/// DataRepository를 테스트를 위해 초기화 (리플렉션 사용)
|
|
/// </summary>
|
|
private void ClearDataRepository()
|
|
{
|
|
var repositoryType = typeof(DataRepository);
|
|
var instanceField = repositoryType.GetField("instance",
|
|
BindingFlags.NonPublic | BindingFlags.Static);
|
|
|
|
if (instanceField != null)
|
|
{
|
|
// Lazy<DataRepository> 필드 값을 가져옴
|
|
var lazyInstance = instanceField.GetValue(null);
|
|
|
|
// Lazy<T>의 Value 속성을 통해 인스턴스 접근
|
|
var instance = lazyInstance.GetType().GetProperty("Value").GetValue(lazyInstance);
|
|
|
|
// dataObjects 딕셔너리 필드 가져오기
|
|
var dataObjectsField = repositoryType.GetField("dataObjects",
|
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
// 딕셔너리 접근하여 Clear 메서드 호출
|
|
var dataObjects = dataObjectsField.GetValue(instance) as Dictionary<string, IDataObject>;
|
|
dataObjects.Clear();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Excute 메소드에서 JObject 응답을 처리하는 기능 테스트
|
|
/// </summary>
|
|
public async UniTask Excute_WithJObjectResponse_ProcessesDataCorrectly()
|
|
{
|
|
// Arrange
|
|
bool handlerCalled = false;
|
|
DataObject? receivedData = null;
|
|
|
|
var mockResponse = @"{""message"": ""Success"", ""data"": {""name"": ""테스트"", ""value"": 123}}";
|
|
|
|
// DataMask와 DataMapper 설정
|
|
var dataMask = new DataMask();
|
|
dataMask["name"] = "이름";
|
|
dataMask["value"] = 0;
|
|
|
|
var dataMapper = new DataMapper(dataMask);
|
|
|
|
// HttpRequestConfig 설정
|
|
var info = new HttpRequestConfig("http://test.com")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler((data) =>
|
|
{
|
|
Debug.Log("핸들러 호출됨");
|
|
handlerCalled = true;
|
|
if (data is DataObject dataObject)
|
|
{
|
|
receivedData = dataObject;
|
|
}
|
|
});
|
|
|
|
pipeLine.Add("testKey", info);
|
|
|
|
try
|
|
{
|
|
// MockHttpRequester에 테스트용 응답 설정
|
|
MockHttpRequester.SetResponse("http://test.com", mockResponse);
|
|
|
|
// Act
|
|
await pipeLine.Excute("testKey");
|
|
|
|
// Assert
|
|
Check(handlerCalled, "핸들러가 호출되지 않았습니다.");
|
|
Check(receivedData != null, "핸들러에 전달된 데이터가 null입니다.");
|
|
Check(receivedData?.GetString("name") == "테스트", $"{receivedData?.GetString("name")} != 테스트");
|
|
Check(receivedData?.GetInt("value") == 123, $"{receivedData?.GetInt("value")} != 123");
|
|
}
|
|
finally
|
|
{
|
|
// 설정한 응답 정리
|
|
MockHttpRequester.ClearResponses("http://test.com");
|
|
await pipeLine.RemoveAsync("testKey");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Excute 메소드에서 JArray 응답을 처리하는 기능 테스트
|
|
/// </summary>
|
|
public async UniTask Excute_WithJArrayResponse_ProcessesDataCorrectly()
|
|
{
|
|
// Arrange
|
|
bool handlerCalled = false;
|
|
IDataObject? receivedData = null;
|
|
|
|
var mockResponse = @"{""message"": ""Success"", ""data"": [{""name"": ""항목1"", ""value"": 10}, {""name"": ""항목2"", ""value"": 20}]}";
|
|
|
|
// 배열용 DataMask 설정
|
|
var dataMask = new DataMask
|
|
{
|
|
["name"] = "이름",
|
|
["value"] = 0
|
|
};
|
|
|
|
var dataMapper = new DataMapper(dataMask);
|
|
|
|
// HttpRequestConfig 설정
|
|
var info = new HttpRequestConfig("http://test.com")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler((data) =>
|
|
{
|
|
handlerCalled = true;
|
|
receivedData = data;
|
|
});
|
|
|
|
pipeLine.Add("testArrayKey", info);
|
|
|
|
try
|
|
{
|
|
// MockHttpRequester에 테스트용 응답 설정
|
|
MockHttpRequester.SetResponse("http://test.com", mockResponse);
|
|
|
|
// Act
|
|
await pipeLine.Excute("testArrayKey");
|
|
|
|
|
|
// Assert
|
|
Check(handlerCalled, "핸들러가 호출되지 않았습니다.");
|
|
Check(receivedData != null, "핸들러에 전달된 데이터가 null입니다.");
|
|
Check(receivedData is DataArray, "결과가 DataArray가 아닙니다.");
|
|
|
|
var dataArray = receivedData as DataArray;
|
|
Check(dataArray?.Count == 2, $"{dataArray?.Count} != 2");
|
|
Check(dataArray?[0].GetString("name") == "항목1", $"{dataArray?[0].GetString("name")} != 항목1");
|
|
Check(dataArray?[0].GetInt("value") == 10, $"{dataArray?[0].GetInt("value")} != 10");
|
|
Check(dataArray?[1].GetString("name") == "항목2", $"{dataArray?[1].GetString("name")} != 항목2");
|
|
Check(dataArray?[1].GetInt("value") == 20, $"{dataArray?[1].GetInt("value")} != 20");
|
|
}
|
|
finally
|
|
{
|
|
// 설정한 응답 정리
|
|
MockHttpRequester.ClearResponses("http://test.com");
|
|
await pipeLine.RemoveAsync("testArrayKey");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Excute 메소드에서 존재하지 않는 키를 호출했을 때의 동작 테스트
|
|
/// </summary>
|
|
public void Excute_WithNonExistingKey_DoesNothing()
|
|
{
|
|
// Arrange - 의도적으로 아무 것도 설정하지 않음
|
|
|
|
// Act - 예외가 발생하지 않아야 함
|
|
try { pipeLine.Excute("nonExistingKey"); Debug.Log("[OK] 존재하지 않는 키 호출 시 예외 없음"); }
|
|
catch (Exception ex) { throw new Exception($"예외가 발생하지 않아야 합니다: {ex.Message}"); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// UVC.Tests.MockHttpRequester를 사용하여 AGV 데이터 파싱 테스트
|
|
/// </summary>
|
|
public async UniTask Test_Excute_AgvDataParsing()
|
|
{
|
|
// Arrange
|
|
bool handlerCalled = false;
|
|
IDataObject? receivedData = null;
|
|
string agvUrl = "http://api.example.com/agv"; // MockHttpRequester는 url에 "agv"가 포함된 경우 AGV 관련 응답 반환
|
|
|
|
// 배열용 DataMask 설정 (AGV 데이터 구조에 맞춤)
|
|
var dataMask = new DataMask
|
|
{
|
|
["VHL_NAME"] = "차량명",
|
|
["AGV_IDX"] = "인덱스",
|
|
["B_INSTALL"] = "설치여부",
|
|
["NODE_ID"] = "노드ID",
|
|
["REAL_ID"] = "실제ID",
|
|
["VHL_STATE"] = "상태",
|
|
["BAY_LIST"] = "베이리스트"
|
|
};
|
|
|
|
var dataMapper = new DataMapper(dataMask);
|
|
|
|
// HttpRequestConfig 설정
|
|
var info = new HttpRequestConfig(agvUrl, "get")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler((data) =>
|
|
{
|
|
handlerCalled = true;
|
|
receivedData = data;
|
|
});
|
|
|
|
pipeLine.Add("agvData", info);
|
|
|
|
// Act
|
|
await pipeLine.Excute("agvData");
|
|
|
|
// Assert
|
|
Check(handlerCalled, "핸들러가 호출되지 않았습니다.");
|
|
Check(receivedData != null, "데이터가 null입니다.");
|
|
Check(receivedData is DataArray, "결과가 DataArray가 아닙니다.");
|
|
|
|
var dataArray = receivedData as DataArray;
|
|
Check(dataArray?.Count > 0, "데이터 배열이 비어 있습니다.");
|
|
|
|
// 첫 번째 항목 확인
|
|
Check(dataArray?[0].GetString("VHL_NAME") != null, "VHL_NAME이 null입니다.");
|
|
Check(dataArray?[0].GetString("AGV_IDX") != null, "AGV_IDX가 null입니다.");
|
|
Check(dataArray?[0].GetString("B_INSTALL") != null, "B_INSTALL이 null입니다.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// UVC.Tests.MockHttpRequester를 사용하여 알람 데이터 파싱 테스트
|
|
/// </summary>
|
|
public async UniTask Test_Excute_AlarmDataParsing()
|
|
{
|
|
// Arrange
|
|
bool handlerCalled = false;
|
|
IDataObject? receivedData = null;
|
|
string alarmUrl = "http://api.example.com/alarm"; // MockHttpRequester는 url에 "alarm"이 포함된 경우 알람 관련 응답 반환
|
|
|
|
// 배열용 DataMask 설정 (알람 데이터 구조에 맞춤)
|
|
var dataMask = new DataMask
|
|
{
|
|
["ID"] = "알람ID",
|
|
["ALARM_TYPE"] = "알람타입",
|
|
["LEVEL"] = "심각도",
|
|
["STATE"] = "상태",
|
|
["MESSAGE"] = "메시지",
|
|
["CODE"] = "코드",
|
|
["ICON"] = "아이콘",
|
|
["SET_TIME"] = "발생시간"
|
|
};
|
|
|
|
var dataMapper = new DataMapper(dataMask);
|
|
|
|
// HttpRequestConfig 설정
|
|
var info = new HttpRequestConfig(alarmUrl, "get")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler((data) =>
|
|
{
|
|
handlerCalled = true;
|
|
receivedData = data;
|
|
});
|
|
|
|
pipeLine.Add("alarmData", info);
|
|
|
|
try
|
|
{
|
|
// Act
|
|
await pipeLine.Excute("alarmData");
|
|
|
|
// Assert
|
|
Check(handlerCalled, "핸들러가 호출되지 않았습니다.");
|
|
Check(receivedData != null, "데이터가 null입니다.");
|
|
Check(receivedData is DataArray, "결과가 DataArray가 아닙니다.");
|
|
|
|
var dataArray = receivedData as DataArray;
|
|
Check(dataArray?.Count > 0, "데이터 배열이 비어 있습니다.");
|
|
|
|
// 첫 번째 항목 확인
|
|
Check(dataArray?[0].GetString("ID") != null, "ID가 null입니다.");
|
|
Check(dataArray?[0].GetString("ALARM_TYPE") != null, "ALARM_TYPE이 null입니다.");
|
|
Check(dataArray?[0].GetString("LEVEL") != null, "LEVEL이 null입니다.");
|
|
Check(dataArray?[0].GetString("MESSAGE") != null, "MESSAGE가 null입니다.");
|
|
Check(dataArray?[0].GetString("CODE") != null, "CODE가 null입니다.");
|
|
Check(dataArray?[0].GetString("ICON") != null, "ICON이 null입니다.");
|
|
Check(dataArray?[0].GetString("SET_TIME") != null, "SET_TIME이 null입니다.");
|
|
}
|
|
finally
|
|
{
|
|
await pipeLine.RemoveAsync("alarmData");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// UVC.Tests.MockHttpRequester를 사용하여 여러 URL 유형에 대한 동시 테스트
|
|
/// </summary>
|
|
public async UniTask Test_Excute_MultipleDataTypes()
|
|
{
|
|
// Arrange
|
|
int handlerCallCount = 0;
|
|
Dictionary<string, IDataObject?> results = new Dictionary<string, IDataObject?>();
|
|
|
|
// 각 데이터 타입별 URL
|
|
Dictionary<string, string> urls = new Dictionary<string, string>
|
|
{
|
|
{ "agv", "http://api.example.com/agv" },
|
|
{ "equipment", "http://api.example.com/equipment" },
|
|
{ "alarm", "http://api.example.com/alarm" }
|
|
};
|
|
|
|
// 각 데이터 타입에 대한 DataMask 설정
|
|
Dictionary<string, DataMask> dataMasks = new Dictionary<string, DataMask>
|
|
{
|
|
{
|
|
"agv", new DataMask
|
|
{
|
|
["0"] = new DataMask { ["VHL_NAME"] = "차량명", ["AGV_IDX"] = "인덱스" }
|
|
}
|
|
},
|
|
{
|
|
"equipment", new DataMask
|
|
{
|
|
["0"] = new DataMask { ["EQP_ID"] = "장비ID", ["KOR_EQP_NAME"] = "장비명" }
|
|
}
|
|
},
|
|
{
|
|
"alarm", new DataMask
|
|
{
|
|
["0"] = new DataMask { ["ID"] = "알람ID", ["ALARM_TYPE"] = "알람타입" }
|
|
}
|
|
}
|
|
};
|
|
|
|
// 각 데이터 타입별 HttpRequestConfig 설정 및 등록
|
|
foreach (var item in urls)
|
|
{
|
|
string key = item.Key;
|
|
var info = new HttpRequestConfig(item.Value, "get")
|
|
.SetDataMapper(new DataMapper(dataMasks[key]))
|
|
.SetSuccessHandler((data) =>
|
|
{
|
|
handlerCallCount++;
|
|
results[key] = data;
|
|
});
|
|
|
|
pipeLine.Add(key, info);
|
|
await pipeLine.Excute(key);
|
|
}
|
|
|
|
|
|
// Assert
|
|
Check(handlerCallCount == urls.Count, $"{handlerCallCount} != {urls.Count}, 모든 핸들러가 호출되지 않았습니다.");
|
|
Check(results.Count == urls.Count, $"{results.Count} != {urls.Count}, 모든 결과가 수집되지 않았습니다.");
|
|
|
|
// 각 데이터 타입별 결과 확인
|
|
foreach (var result in results)
|
|
{
|
|
Check(result.Value != null, $"{result.Key} 데이터가 null입니다.");
|
|
Check(result.Value is DataArray, $"{result.Key} 결과가 DataArray가 아닙니다.");
|
|
|
|
var dataArray = result.Value as DataArray;
|
|
Check(dataArray?.Count > 0, $"{result.Key} 데이터 배열이 비어 있습니다.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// UVC.Tests.MockHttpRequester를 사용한 운반대(Carrier) 데이터 테스트
|
|
/// </summary>
|
|
public async UniTask Test_Excute_CarrierDataParsing()
|
|
{
|
|
// Arrange
|
|
bool handlerCalled = false;
|
|
IDataObject? receivedData = null;
|
|
string testUrl = "http://api.example.com/carrier"; // url에 "carrier"가 포함된 경우 캐리어 관련 응답 반환
|
|
|
|
// DataMask와 DataMapper 설정
|
|
var dataMask = new DataMask
|
|
{
|
|
["MAIN_CARR_ID"] = "캐리어ID",
|
|
["SUB_CARR_ID"] = "서브ID",
|
|
["CARR_SEQ"] = "순번",
|
|
["CARR_USE"] = "사용상태"
|
|
};
|
|
|
|
var dataMapper = new DataMapper(dataMask);
|
|
|
|
// HttpRequestConfig 설정
|
|
var info = new HttpRequestConfig(testUrl, "get")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler((data) =>
|
|
{
|
|
handlerCalled = true;
|
|
receivedData = data;
|
|
});
|
|
|
|
pipeLine.Add("testCarrierData", info);
|
|
|
|
// Act
|
|
await pipeLine.Excute("testCarrierData");
|
|
|
|
// Assert
|
|
Check(handlerCalled, "핸들러가 호출되지 않았습니다.");
|
|
Check(receivedData != null, "응답 데이터가 null입니다.");
|
|
Check(receivedData is DataArray, "응답이 DataArray가 아닙니다.");
|
|
|
|
var dataArray = receivedData as DataArray;
|
|
Check(dataArray?.Count > 0, "데이터 배열이 비어있습니다.");
|
|
|
|
// 첫 번째 캐리어 데이터 확인
|
|
Check(dataArray?[0].GetString("MAIN_CARR_ID") != null, "캐리어ID가 null입니다.");
|
|
Check(dataArray?[0].GetString("SUB_CARR_ID") != null, "서브ID가 null입니다.");
|
|
Check(dataArray?[0].GetString("CARR_SEQ") != null, "순번이 null입니다.");
|
|
Check(dataArray?[0].GetString("CARR_USE") != null, "사용상태가 null입니다.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// UVC.Tests.MockHttpRequester를 사용하여 BaseInfo 데이터 파싱 테스트
|
|
/// </summary>
|
|
public async UniTask Test_Excute_BaseInfoDataParsing()
|
|
{
|
|
// Arrange
|
|
bool handlerCalled = false;
|
|
IDataObject? receivedData = null;
|
|
string baseInfoUrl = "http://api.example.com/baseinfo"; // url에 "baseinfo"가 포함된 경우 기본 정보 응답 반환
|
|
|
|
// BaseInfo에서 테스트할 주요 섹션 (AGV, EQUIPMENT, ALARM 등)에 대한 DataMask 설정
|
|
var dataMask = new DataMask
|
|
{
|
|
["AGV"] = new DataMask
|
|
{
|
|
["0"] = new DataMask
|
|
{
|
|
["VHL_NAME"] = "차량명",
|
|
["AGV_IDX"] = "인덱스",
|
|
["B_INSTALL"] = "설치여부",
|
|
["NODE_ID"] = "노드ID",
|
|
["VHL_STATE"] = "상태"
|
|
}
|
|
},
|
|
["EQUIPMENT"] = new DataMask
|
|
{
|
|
["0"] = new DataMask
|
|
{
|
|
["EQP_ID"] = "장비ID",
|
|
["KOR_EQP_NAME"] = "장비명",
|
|
["STATE_ID"] = "상태",
|
|
["NTW_STS"] = "네트워크상태"
|
|
}
|
|
},
|
|
["ALARM"] = new DataMask
|
|
{
|
|
["0"] = new DataMask
|
|
{
|
|
["ID"] = "알람ID",
|
|
["ALARM_TYPE"] = "알람타입",
|
|
["LEVEL"] = "심각도",
|
|
["MESSAGE"] = "메시지"
|
|
}
|
|
}
|
|
};
|
|
|
|
var dataMapper = new DataMapper(dataMask);
|
|
|
|
// HttpRequestConfig 설정
|
|
var info = new HttpRequestConfig(baseInfoUrl, "get")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler((data) =>
|
|
{
|
|
handlerCalled = true;
|
|
receivedData = data;
|
|
});
|
|
|
|
pipeLine.Add("baseInfoData", info);
|
|
|
|
try
|
|
{
|
|
// Act
|
|
await pipeLine.Excute("baseInfoData");
|
|
|
|
// Assert
|
|
Check(handlerCalled, "핸들러가 호출되지 않았습니다.");
|
|
Check(receivedData != null, "데이터가 null입니다.");
|
|
Check(receivedData is DataObject, "결과가 DataObject가 아닙니다.");
|
|
|
|
var dataObject = receivedData as DataObject;
|
|
|
|
// AGV 데이터 검증
|
|
Check(dataObject?.Get("AGV") != null, "AGV 데이터가 없습니다.");
|
|
var agvData = dataObject?.Get("AGV") as DataArray;
|
|
Check(agvData?.Count > 0, "AGV 데이터 배열이 비어 있습니다.");
|
|
Check(agvData?[0].GetString("VHL_NAME") != null, "VHL_NAME 필드가 없습니다.");
|
|
Check(agvData?[0].GetString("AGV_IDX") != null, "AGV_IDX 필드가 없습니다.");
|
|
Check(agvData?[0].GetString("B_INSTALL") != null, "B_INSTALL 필드가 없습니다.");
|
|
|
|
// EQUIPMENT 데이터 검증
|
|
Check(dataObject?.Get("EQUIPMENT") != null, "EQUIPMENT 데이터가 없습니다.");
|
|
var equipmentData = dataObject?.Get("EQUIPMENT") as DataArray;
|
|
Check(equipmentData?.Count > 0, "EQUIPMENT 데이터 배열이 비어 있습니다.");
|
|
Check(equipmentData?[0].GetString("EQP_ID") != null, "EQP_ID 필드가 없습니다.");
|
|
Check(equipmentData?[0].GetString("KOR_EQP_NAME") != null, "KOR_EQP_NAME 필드가 없습니다.");
|
|
Check(equipmentData?[0].GetString("STATE_ID") != null, "STATE_ID 필드가 없습니다.");
|
|
|
|
// ALARM 데이터 검증
|
|
Check(dataObject?.Get("ALARM") != null, "ALARM 데이터가 없습니다.");
|
|
var alarmData = dataObject?.Get("ALARM") as DataArray;
|
|
Check(alarmData?.Count > 0, "ALARM 데이터 배열이 비어 있습니다.");
|
|
Check(alarmData?[0].GetString("ID") != null, "ID 필드가 없습니다.");
|
|
Check(alarmData?[0].GetString("ALARM_TYPE") != null, "ALARM_TYPE 필드가 없습니다.");
|
|
Check(alarmData?[0].GetString("LEVEL") != null, "LEVEL 필드가 없습니다.");
|
|
Check(alarmData?[0].GetString("MESSAGE") != null, "MESSAGE 필드가 없습니다.");
|
|
}
|
|
finally
|
|
{
|
|
await pipeLine.RemoveAsync("baseInfoData");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 반복 실행 기능을 테스트합니다.
|
|
/// </summary>
|
|
public async UniTask Test_Excute_WithRepeatExecution()
|
|
{
|
|
// Arrange
|
|
int handlerCallCount = 0;
|
|
List<string> receivedResponses = new List<string>();
|
|
string testUrl = "http://test.com/repeat";
|
|
int expectedCallCount = 3;
|
|
int repeatInterval = 100; // 테스트를 빠르게 진행하기 위해 간격을 짧게 설정
|
|
|
|
// 여러 응답을 순차적으로 반환하기 위한 응답 데이터 설정
|
|
string[] mockResponses = new string[]
|
|
{
|
|
@"{""message"": ""Success"", ""data"": {""Id"": 1, ""status"": ""pending"", ""timestamp"": ""2025-06-09T10:00:00Z""}}",
|
|
@"{""message"": ""Success"", ""data"": {""Id"": 1, ""status"": ""processing"", ""timestamp"": ""2025-06-09T10:00:10Z""}}",
|
|
@"{""message"": ""Success"", ""data"": {""Id"": 1, ""status"": ""completed"", ""timestamp"": ""2025-06-09T10:00:20Z""}}"
|
|
};
|
|
|
|
// Mock 응답 설정
|
|
MockHttpRequester.SetResponse(testUrl, mockResponses[0]);
|
|
|
|
// DataMask와 DataMapper 설정
|
|
var dataMask = new DataMask();
|
|
dataMask["Id"] = 0;
|
|
dataMask["status"] = "";
|
|
dataMask["timestamp"] = "";
|
|
|
|
var dataMapper = new DataMapper(dataMask);
|
|
|
|
// 반복 실행 설정을 포함한 HttpRequestConfig 생성
|
|
var info = new HttpRequestConfig(testUrl, "get")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler(async (data) =>
|
|
{
|
|
handlerCallCount++;
|
|
if (data is DataObject dataObject)
|
|
{
|
|
receivedResponses.Add(dataObject.GetString("status"));
|
|
}
|
|
|
|
// 반복 실행 중단
|
|
if (handlerCallCount >= expectedCallCount)
|
|
{
|
|
await pipeLine.StopRepeat("repeatTest");
|
|
}
|
|
else
|
|
{
|
|
MockHttpRequester.SetResponse(testUrl, mockResponses[handlerCallCount]);
|
|
}
|
|
})
|
|
.SetRepeat(true, expectedCallCount, repeatInterval, false);
|
|
|
|
pipeLine.UseMockup = true;
|
|
pipeLine.Add("repeatTest", info);
|
|
|
|
try
|
|
{
|
|
// Act
|
|
await pipeLine.Excute("repeatTest");
|
|
|
|
// 반복 작업이 완료될 때까지 대기
|
|
// (실제 상황에서는 이렇게 기다리지 않지만 테스트를 위해 필요)
|
|
await UniTask.Delay((repeatInterval * expectedCallCount) + 1000 * expectedCallCount);
|
|
|
|
// Assert
|
|
Check(handlerCallCount == expectedCallCount, $"{handlerCallCount} != {expectedCallCount}, 핸들러 호출 횟수가 예상과 다릅니다");
|
|
|
|
// 응답이 순차적으로 처리되었는지 확인
|
|
Check(receivedResponses[0] == "pending", $"{receivedResponses[0]} != pending, 첫 번째 응답이 올바르지 않습니다");
|
|
Check(receivedResponses[1] == "processing", $"{receivedResponses[1]} != processing, 두 번째 응답이 올바르지 않습니다");
|
|
Check(receivedResponses[2] == "completed", $"{receivedResponses[2]} != completed, 세 번째 응답이 올바르지 않습니다");
|
|
}
|
|
finally
|
|
{
|
|
// 테스트 정리
|
|
await pipeLine.RemoveAsync("repeatTest");
|
|
MockHttpRequester.ClearResponses(testUrl);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 실행 중인 반복 요청을 중지하는 기능을 테스트합니다.
|
|
/// </summary>
|
|
public async UniTask Test_StopRepeat_StopsExecutionCorrectly()
|
|
{
|
|
// Arrange
|
|
int handlerCallCount = 0;
|
|
string testUrl = "http://test.com/repeat-stop";
|
|
int repeatInterval = 100;
|
|
|
|
// Mock 응답 설정
|
|
string mockResponse = @"{""message"": ""Success"", ""data"": {""Id"": 2, ""status"": ""running"", ""timestamp"": ""2025-06-09T11:00:00Z""}}";
|
|
MockHttpRequester.SetResponse(testUrl, mockResponse);
|
|
|
|
// DataMask와 DataMapper 설정
|
|
var dataMask = new DataMask();
|
|
dataMask["Id"] = 0;
|
|
dataMask["status"] = "";
|
|
dataMask["timestamp"] = "";
|
|
|
|
var dataMapper = new DataMapper(dataMask);
|
|
|
|
// 무한 반복 설정을 포함한 HttpRequestConfig 생성
|
|
var info = new HttpRequestConfig(testUrl, "get")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler((data) => { handlerCallCount++; })
|
|
.SetRepeat(true, 0, repeatInterval, false); // 무한 반복 (repeatCount = 0)
|
|
|
|
pipeLine.UseMockup = true;
|
|
pipeLine.Add("infiniteRepeatTest", info);
|
|
|
|
try
|
|
{
|
|
// Act
|
|
await pipeLine.Excute("infiniteRepeatTest");
|
|
|
|
// 몇 번의 반복 실행이 될 때까지 대기
|
|
await UniTask.Delay(repeatInterval * 2 + 50);
|
|
|
|
// 반복 중지
|
|
await pipeLine.StopRepeat("infiniteRepeatTest");
|
|
|
|
// 현재 호출 횟수 기록
|
|
int callCountBeforeStop = handlerCallCount;
|
|
|
|
// 더 이상 호출이 발생하지 않는지 확인하기 위해 추가 대기
|
|
await UniTask.Delay(repeatInterval * 2 + 50);
|
|
|
|
// Assert
|
|
Check(callCountBeforeStop > 0, "반복 호출이 발생하지 않았습니다");
|
|
Check(callCountBeforeStop == handlerCallCount, $"{callCountBeforeStop} != {handlerCallCount}, 반복 중지 후에도 호출이 계속 발생했습니다");
|
|
}
|
|
finally
|
|
{
|
|
// 테스트 정리
|
|
await pipeLine.RemoveAsync("infiniteRepeatTest");
|
|
MockHttpRequester.ClearResponses(testUrl);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 여러 개의 반복 요청을 동시에 실행하고 개별적으로 중지하는 기능을 테스트합니다.
|
|
/// </summary>
|
|
public async UniTask Test_MultipleRepeatingRequests_ManagedIndependently()
|
|
{
|
|
// Arrange
|
|
int handlerCallCount1 = 0;
|
|
int handlerCallCount2 = 0;
|
|
|
|
string testUrl1 = "http://test.com/repeat1";
|
|
string testUrl2 = "http://test.com/repeat2";
|
|
|
|
int repeatInterval1 = 100;
|
|
int repeatInterval2 = 150;
|
|
|
|
// Mock 응답 설정
|
|
string mockResponse1 = @"{""message"": ""Success"", ""data"": {""Id"": 3, ""name"": ""작업1""}}";
|
|
string mockResponse2 = @"{""message"": ""Success"", ""data"": {""Id"": 4, ""name"": ""작업2""}}";
|
|
|
|
MockHttpRequester.SetResponse(testUrl1, mockResponse1);
|
|
MockHttpRequester.SetResponse(testUrl2, mockResponse2);
|
|
|
|
// DataMask 설정
|
|
var dataMask = new DataMask();
|
|
dataMask["Id"] = 0;
|
|
dataMask["name"] = "";
|
|
|
|
var dataMapper = new DataMapper(dataMask);
|
|
|
|
// 두 개의 반복 요청 설정
|
|
var info1 = new HttpRequestConfig(testUrl1, "get")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler((data) => { handlerCallCount1++; })
|
|
.SetRepeat(true, 0, repeatInterval1, false);
|
|
|
|
var info2 = new HttpRequestConfig(testUrl2, "get")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler((data) => { handlerCallCount2++; })
|
|
.SetRepeat(true, 0, repeatInterval2, false);
|
|
|
|
pipeLine.UseMockup = true;
|
|
pipeLine.Add("repeatTest1", info1);
|
|
pipeLine.Add("repeatTest2", info2);
|
|
|
|
try
|
|
{
|
|
// Act
|
|
await pipeLine.Excute("repeatTest1");
|
|
await pipeLine.Excute("repeatTest2");
|
|
|
|
// 두 요청 모두 몇 번 실행되도록 대기
|
|
await UniTask.Delay(Math.Max(repeatInterval1, repeatInterval2) * 3 + 100 * 3 + 50);//100 네트워크 지연, 50 추가 여유
|
|
|
|
// 첫 번째 반복만 중지
|
|
await pipeLine.StopRepeat("repeatTest1");
|
|
|
|
// 호출 횟수 기록
|
|
int callCount1AfterStop = handlerCallCount1;
|
|
int callCount2BeforeSecondStop = handlerCallCount2;
|
|
|
|
// 두 번째 반복이 계속 실행되는지 확인하기 위해 대기
|
|
await UniTask.Delay(repeatInterval2 * 2 + 100 * 2 + 50);
|
|
// Assert
|
|
Check(callCount1AfterStop > 0, "첫 번째 반복 요청 실행이 발생하지 않았습니다");
|
|
Check(handlerCallCount2 > callCount2BeforeSecondStop, "두 번째 반복이 계속 실행되지 않았습니다");
|
|
Check(callCount1AfterStop == handlerCallCount1, $"{callCount1AfterStop} != {handlerCallCount1}, 첫 번째 반복이 중지되지 않았습니다");
|
|
// 두 번째 반복도 중지
|
|
await pipeLine.StopRepeat("repeatTest2");
|
|
// 호출 횟수 다시 기록
|
|
int callCount2AfterStop = handlerCallCount2;
|
|
// 더 이상 호출이 없는지 확인하기 위해 대기
|
|
await UniTask.Delay(Math.Max(repeatInterval1, repeatInterval2) * 2 + 100 * 2 + 50);
|
|
// 모든 호출이 중지되었는지 확인
|
|
Check(callCount1AfterStop == handlerCallCount1, $"{callCount1AfterStop} != {handlerCallCount1}, 첫 번째 반복이 계속되었습니다");
|
|
Check(callCount2AfterStop == handlerCallCount2, $"{callCount2AfterStop} != {handlerCallCount2}, 두 번째 반복이 계속되었습니다");
|
|
}
|
|
finally
|
|
{
|
|
// 테스트 정리
|
|
await pipeLine.RemoveAsync("repeatTest1");
|
|
await pipeLine.RemoveAsync("repeatTest2");
|
|
MockHttpRequester.ClearResponses(testUrl1);
|
|
MockHttpRequester.ClearResponses(testUrl2);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 지정된 횟수만큼 반복 실행 후 자동 중단되는 기능을 테스트합니다.
|
|
/// </summary>
|
|
public async UniTask Test_RepeatWithCount_StopsAutomatically()
|
|
{
|
|
// Arrange
|
|
int handlerCallCount = 0;
|
|
List<IDataObject?> receivedData = new List<IDataObject?>();
|
|
|
|
string testUrl = "http://test.com/repeat-count";
|
|
int repeatCount = 3;
|
|
int repeatInterval = 100;
|
|
|
|
// Mock 응답 설정
|
|
string mockResponse = @"{""message"": ""Success"", ""data"": {""Id"": 5, ""message"": ""자동 중단 테스트""}}";
|
|
MockHttpRequester.SetResponse(testUrl, mockResponse);
|
|
|
|
// DataMask 설정
|
|
var dataMask = new DataMask();
|
|
dataMask["Id"] = 0;
|
|
dataMask["message"] = "";
|
|
|
|
var dataMapper = new DataMapper(dataMask);
|
|
|
|
// 반복 횟수가 지정된 HttpRequestConfig 생성
|
|
var info = new HttpRequestConfig(testUrl, "get")
|
|
.SetDataMapper(dataMapper)
|
|
.SetSuccessHandler((data) =>
|
|
{
|
|
handlerCallCount++;
|
|
receivedData.Add(data);
|
|
})
|
|
.SetRepeat(true, repeatCount, repeatInterval, false);
|
|
|
|
pipeLine.UseMockup = true;
|
|
pipeLine.Add("countedRepeatTest", info);
|
|
|
|
try
|
|
{
|
|
// Act
|
|
await pipeLine.Excute("countedRepeatTest");
|
|
|
|
// 지정된 횟수의 반복이 완료될 때까지 충분히 대기
|
|
await UniTask.Delay(repeatInterval * (repeatCount + 1) + 50 + 100 * (repeatCount + 1));
|
|
|
|
// Assert
|
|
Check(handlerCallCount == repeatCount, $"{handlerCallCount} != {repeatCount}, 지정된 횟수만큼 반복 실행되지 않았습니다");
|
|
Check(receivedData.Count == repeatCount, $"{receivedData.Count} != {repeatCount}, 기대한 데이터 수와 다릅니다");
|
|
|
|
// 각 응답이 올바르게 처리되었는지 확인
|
|
var data = receivedData[0];
|
|
Check(data != null, "데이터가 null입니다");
|
|
var dataObject = data as DataObject;
|
|
Check(dataObject?.GetInt("Id") == 5, $"{dataObject?.GetInt("Id")} != 5, ID가 올바르지 않습니다");
|
|
Check(dataObject?.GetString("message") == "자동 중단 테스트", $"{dataObject?.GetString("message")} != 자동 중단 테스트, 메시지가 올바르지 않습니다");
|
|
|
|
// 자동으로 제거되었는지 확인
|
|
var repeatTokenSources = GetRepeatTokenSourcesField();
|
|
Check(!repeatTokenSources.ContainsKey("countedRepeatTest"),
|
|
"실행 완료 후 반복 토큰이 제거되지 않았습니다");
|
|
}
|
|
finally
|
|
{
|
|
// 테스트 정리
|
|
await pipeLine.RemoveAsync("countedRepeatTest");
|
|
MockHttpRequester.ClearResponses(testUrl);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// HttpDataFetcher의 private repeatTokenSources 필드 가져오기
|
|
/// </summary>
|
|
private Dictionary<string, CancellationTokenSource> GetRepeatTokenSourcesField()
|
|
{
|
|
var fieldInfo = typeof(HttpDataFetcher).GetField("repeatTokenSources",
|
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
return (Dictionary<string, CancellationTokenSource>)fieldInfo.GetValue(pipeLine);
|
|
}
|
|
|
|
#region HttpResponseMask Tests
|
|
|
|
/// <summary>
|
|
/// HttpResponseMask.Apply가 성공적인 응답을 올바르게 처리하는지 테스트합니다.
|
|
/// </summary>
|
|
public void HttpResponseMask_Apply_SuccessfulResponse_ReturnsSuccessWithData()
|
|
{
|
|
// Arrange
|
|
var responseMask = new HttpResponseMask(); // 기본값 사용 (successKey="message", successValue="success", dataKey="data")
|
|
var jsonResponse = @"{""message"": ""success"", ""data"": {""key"":""value""}}";
|
|
var expectedData = @"{""key"":""value""}";
|
|
|
|
// Act
|
|
var result = responseMask.Apply(jsonResponse);
|
|
|
|
// Assert
|
|
Check(result.IsSuccess, "결과가 성공이어야 합니다.");
|
|
Check(result.Data == expectedData, $"{result.Data} != {expectedData}, 추출된 데이터가 예상과 다릅니다.");
|
|
Check(result.Message == null, "성공 시 메시지는 null이어야 합니다.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// HttpResponseMask.Apply가 잘못된 성공 값으로 실패 응답을 올바르게 처리하는지 테스트합니다.
|
|
/// </summary>
|
|
public void HttpResponseMask_Apply_FailedResponse_WrongSuccessValue_ReturnsFailWithMessage()
|
|
{
|
|
// Arrange
|
|
var responseMask = new HttpResponseMask();
|
|
var jsonResponse = @"{""message"": ""failed"", ""data"": {""key"": ""value""}}"; // successValue가 "success"가 아님
|
|
|
|
// Act
|
|
var result = responseMask.Apply(jsonResponse);
|
|
|
|
// Assert
|
|
Check(!result.IsSuccess, "결과가 실패여야 합니다.");
|
|
Check(result.Data == null, "실패 시 데이터는 null이어야 합니다.");
|
|
Check(result.Message == jsonResponse, $"{result.Message} != {jsonResponse}, 실패 시 메시지는 원본 응답이어야 합니다.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// HttpResponseMask.Apply가 성공 키가 없는 실패 응답을 올바르게 처리하는지 테스트합니다.
|
|
/// </summary>
|
|
public void HttpResponseMask_Apply_FailedResponse_MissingSuccessKey_ReturnsFailWithMessage()
|
|
{
|
|
// Arrange
|
|
var responseMask = new HttpResponseMask();
|
|
var jsonResponse = @"{""error"": ""some error"", ""data"": {""key"": ""value""}}"; // "message" 키가 없음
|
|
|
|
// Act
|
|
var result = responseMask.Apply(jsonResponse);
|
|
|
|
// Assert
|
|
Check(!result.IsSuccess, "결과가 실패여야 합니다.");
|
|
Check(result.Data == null, "실패 시 데이터는 null이어야 합니다.");
|
|
Check(result.Message == jsonResponse, $"{result.Message} != {jsonResponse}, 실패 시 메시지는 원본 응답이어야 합니다.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// HttpResponseMask.Apply가 데이터 키가 없는 실패 응답을 올바르게 처리하는지 테스트합니다.
|
|
/// (현재 구현상 successKey 조건만 만족하면 dataKey가 없어도 성공으로 간주하고 Data를 null로 반환할 수 있으므로, 이 테스트는 실패할 수 있습니다.
|
|
/// 요구사항에 따라 이 부분의 동작을 명확히 하고 테스트를 조정해야 합니다.)
|
|
/// </summary>
|
|
public void HttpResponseMask_Apply_FailedResponse_MissingDataKey_ReturnsFailWithMessage()
|
|
{
|
|
// Arrange
|
|
var responseMask = new HttpResponseMask();
|
|
// 성공 키는 있지만 데이터 키가 없는 경우
|
|
var jsonResponse = @"{""message"": ""success"", ""payload"": {""key"": ""value""}}"; // "data" 키가 없음
|
|
|
|
// Act
|
|
var result = responseMask.Apply(jsonResponse);
|
|
|
|
// Assert
|
|
// 현재 Apply 메서드 구현에 따르면, successKey/successValue가 일치하고 dataKey가 없으면
|
|
// IsSuccess = false, Data = null 로 반환됩니다.
|
|
// 만약 dataKey가 필수라면 Apply 메서드 수정 또는 이 테스트의 기대 결과 수정이 필요합니다.
|
|
// 여기서는 현재 구현을 기준으로 테스트합니다.
|
|
Check(!result.IsSuccess, "data 키가 없으면 success 조건 만족 시 실패여야 합니다 (현재 로직 기준).");
|
|
Check(result.Data == null, "data 키가 없는 경우 Data는 null이어야 합니다.");
|
|
Check(result.Message != null, "실패 시 메시지는 null이 아니어야 합니다.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// HttpResponseMask.Apply가 사용자 정의 키를 사용하여 성공적인 응답을 올바르게 처리하는지 테스트합니다.
|
|
/// </summary>
|
|
public void HttpResponseMask_Apply_SuccessfulResponse_CustomKeys_ReturnsSuccessWithData()
|
|
{
|
|
// Arrange
|
|
var responseMask = new HttpResponseMask("status", "ok", "payload");
|
|
var jsonResponse = @"{""status"": ""ok"", ""payload"": {""info"":""custom data""}}";
|
|
var expectedData = @"{""info"":""custom data""}";
|
|
|
|
// Act
|
|
var result = responseMask.Apply(jsonResponse);
|
|
|
|
// Assert
|
|
Check(result.IsSuccess, "결과가 성공이어야 합니다.");
|
|
Check(result.Data == expectedData, $"{result.Data} != {expectedData}, 추출된 데이터가 예상과 다릅니다.");
|
|
Check(result.Message == null, "성공 시 메시지는 null이어야 합니다.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// HttpResponseMask.Apply가 잘못된 JSON 형식의 응답을 처리할 때 예외를 발생하는지 테스트합니다.
|
|
/// </summary>
|
|
public void HttpResponseMask_Apply_InvalidJson_ThrowsException()
|
|
{
|
|
// Arrange
|
|
var responseMask = new HttpResponseMask();
|
|
var invalidJsonResponse = @"{""message"": ""success"", ""data"": {""key"": ""value"""; // 닫는 중괄호 누락
|
|
|
|
// Act - 잘못된 JSON 형식에 대해 JsonReaderException이 발생해야 함
|
|
try
|
|
{
|
|
responseMask.Apply(invalidJsonResponse);
|
|
throw new Exception("검증 실패: JsonReaderException이 발생해야 합니다.");
|
|
}
|
|
catch (Newtonsoft.Json.JsonReaderException)
|
|
{
|
|
// 예상된 예외 — 정상
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DataValidator Tests
|
|
|
|
/// <summary>
|
|
/// DataValidator를 사용하여 유효한 데이터를 성공적으로 처리하는지 테스트합니다.
|
|
/// MockHttpRequester → HttpDataProcessor.ProcessResponse 직접 호출 방식으로 검증합니다.
|
|
/// </summary>
|
|
public async UniTask Test_Excute_WithValidData_ValidatorPasses()
|
|
{
|
|
// Arrange
|
|
bool handlerCalled = false;
|
|
IDataObject? receivedData = null;
|
|
string testUrl = "http://test.com/validator-pass";
|
|
var mockResponse = @"{""message"": ""Success"", ""data"": {""Id"": 1, ""status"": ""active""}}";
|
|
|
|
var dataMapper = new DataMapper(new DataMask { ["Id"] = 0, ["status"] = "" });
|
|
|
|
var validator = new DataValidator();
|
|
validator.AddValidator("status", value => value is string s && s == "active");
|
|
|
|
var info = new HttpRequestConfig(testUrl)
|
|
.SetDataMapper(dataMapper)
|
|
.SetValidator(validator)
|
|
.SetSuccessHandler(data =>
|
|
{
|
|
handlerCalled = true;
|
|
receivedData = data;
|
|
})
|
|
.SetFailHandler(message => Debug.LogError("Fail: " + message));
|
|
|
|
// Act: MockHttpRequester로 응답을 가져와 ProcessResponse에 직접 전달
|
|
string response = await MockHttpRequester.Request<string>(testUrl, "get");
|
|
HttpDataProcessor.ProcessResponse("validatorPassTest", info, mockResponse, CancellationToken.None);
|
|
|
|
await UniTask.Delay(500); // 핸들러가 메인스레드에서 Post되므로 대기
|
|
|
|
// Assert
|
|
Check(handlerCalled, "유효성 검사를 통과했으므로 핸들러가 호출되어야 합니다.");
|
|
Check(receivedData != null, "데이터가 핸들러로 전달되어야 합니다.");
|
|
Check((receivedData as DataObject)?.GetString("status") == "active",
|
|
$"{(receivedData as DataObject)?.GetString("status")} != active");
|
|
}
|
|
|
|
/// <summary>
|
|
/// DataValidator를 사용하여 유효하지 않은 데이터를 필터링하는지 테스트합니다.
|
|
/// </summary>
|
|
public async UniTask Test_Excute_WithInvalidData_ValidatorFails()
|
|
{
|
|
// Arrange
|
|
bool handlerCalled = false;
|
|
string testUrl = "http://test.com/validator-fail";
|
|
var mockResponse = @"{""message"": ""Success"", ""data"": {""Id"": 2, ""status"": ""inactive""}}";
|
|
|
|
var dataMapper = new DataMapper(new DataMask { ["Id"] = 0, ["status"] = "" });
|
|
|
|
var validator = new DataValidator();
|
|
validator.AddValidator("status", value => value is string s && s == "active");
|
|
|
|
var info = new HttpRequestConfig(testUrl)
|
|
.SetDataMapper(dataMapper)
|
|
.SetValidator(validator)
|
|
.SetSuccessHandler(data =>
|
|
{
|
|
handlerCalled = true; // 이 핸들러는 호출되지 않아야 함
|
|
});
|
|
|
|
// Act
|
|
HttpDataProcessor.ProcessResponse("validatorFailTest", info, mockResponse, CancellationToken.None);
|
|
|
|
await UniTask.Delay(500);
|
|
|
|
// Assert
|
|
Check(!handlerCalled, "유효성 검사에 실패했으므로 핸들러가 호출되지 않아야 합니다.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// DataValidator가 배열 데이터에서 유효한 항목만 필터링하는지 테스트합니다.
|
|
/// </summary>
|
|
public async UniTask Test_Excute_WithArrayAndValidator_FiltersInvalidData()
|
|
{
|
|
// Arrange
|
|
bool handlerCalled = false;
|
|
IDataObject? receivedData = null;
|
|
string testUrl = "http://test.com/validator-array";
|
|
var mockResponse = @"{
|
|
""message"": ""Success"",
|
|
""data"": [
|
|
{""Id"": 1, ""value"": 10},
|
|
{""Id"": 2, ""value"": 20},
|
|
{""Id"": 3, ""value"": 5}
|
|
]
|
|
}";
|
|
|
|
var dataMapper = new DataMapper(new DataMask { ["Id"] = 0, ["value"] = 0 });
|
|
|
|
var validator = new DataValidator();
|
|
validator.AddValidator("value", value => value is int v && v > 15);
|
|
|
|
var info = new HttpRequestConfig(testUrl)
|
|
.SetDataMapper(dataMapper)
|
|
.SetValidator(validator)
|
|
.SetSuccessHandler(data =>
|
|
{
|
|
handlerCalled = true;
|
|
receivedData = data;
|
|
});
|
|
|
|
// Act
|
|
HttpDataProcessor.ProcessResponse("validatorArrayTest", info, mockResponse, CancellationToken.None);
|
|
|
|
await UniTask.Delay(500);
|
|
|
|
// Assert
|
|
Check(handlerCalled, "핸들러가 호출되어야 합니다.");
|
|
Check(receivedData != null, "데이터가 핸들러로 전달되어야 합니다.");
|
|
Check(receivedData is DataArray, "결과는 DataArray여야 합니다.");
|
|
|
|
var dataArray = receivedData as DataArray;
|
|
Check(dataArray!.Count == 1, $"{dataArray.Count} != 1, 유효한 항목은 1개여야 합니다.");
|
|
Check(dataArray[0].GetInt("value") == 20,
|
|
$"{dataArray[0].GetInt("value")} != 20, 필터링된 데이터의 값이 올바르지 않습니다.");
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|