Files
XRLib/Assets/Scripts/UVC/Tests/Data/HttpPipeLineTests.cs

1000 lines
40 KiB
C#

#nullable enable
using Cysharp.Threading.Tasks;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UVC.Data;
namespace UVC.Tests.Data
{
/// <summary>
/// HttpPipeLine 클래스의 테스트를 위한 테스트 클래스입니다.
/// </summary>
[TestFixture]
public class HttpPipeLineTests
{
// 테스트에 사용할 HttpPipeLine 인스턴스
private HttpPipeLine pipeLine;
/// <summary>
/// 각 테스트 실행 전에 호출되는 설정 메서드입니다.
/// </summary>
[SetUp]
public void Setup()
{
pipeLine = new HttpPipeLine();
pipeLine.UseMockup = true; // MockHttpRequester 사용 설정
// 테스트를 위한 DataRepository 초기화
ClearDataRepository();
}
/// <summary>
/// 모든 테스트 메서드를 실행하는 메서드입니다.
/// </summary>
/// <remarks>
/// 이 메서드는 클래스의 모든 테스트 메서드를 순차적으로 호출하고
/// 각 테스트의 성공 또는 실패 여부를 로그로 출력합니다.
/// </remarks>
public async UniTask TestAll()
{
Setup();
Debug.Log("===== HttpPipeLine 테스트 시작 =====");
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);
Debug.Log("===== HttpPipeLine 테스트 완료 =====");
}
/// <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>
/// 새로운 HttpPipeLineInfo를 추가하는 테스트
/// </summary>
[Test]
public void Add_NewInfo_AddedSuccessfully()
{
// Arrange
var info = new HttpPipeLineInfo("http://test.com");
// Act
pipeLine.Add("test", info);
// Assert - Dictionary에 추가되었는지 확인 (reflection 사용)
var infoList = GetInfoListField();
Assert.IsTrue(infoList.ContainsKey("test"));
Assert.AreEqual(info, infoList["test"]);
}
/// <summary>
/// 기존에 존재하는 키로 HttpPipeLineInfo를 추가할 때 업데이트 테스트
/// </summary>
[Test]
public void Add_ExistingInfo_UpdatesExistingEntry()
{
// Arrange
var info1 = new HttpPipeLineInfo("http://test1.com");
var info2 = new HttpPipeLineInfo("http://test2.com");
pipeLine.Add("test", info1);
// Act
pipeLine.Add("test", info2);
// Assert
var infoList = GetInfoListField();
Assert.IsTrue(infoList.ContainsKey("test"));
Assert.AreEqual(info2, infoList["test"]);
Assert.AreNotEqual(info1, infoList["test"]);
}
/// <summary>
/// 존재하는 HttpPipeLineInfo를 제거하는 테스트
/// </summary>
[Test]
public async UniTask Remove_ExistingInfo_RemovedSuccessfullyAsync()
{
// Arrange
var info = new HttpPipeLineInfo("http://test.com");
pipeLine.Add("test", info);
// Act
await pipeLine.RemoveAsync("test");
// Assert
var infoList = GetInfoListField();
Assert.IsFalse(infoList.ContainsKey("test"));
}
/// <summary>
/// 존재하지 않는 키에 대한 Remove 호출 테스트
/// </summary>
[Test]
public async UniTask Remove_NonExistingInfo_DoesNothing()
{
// Arrange
var info = new HttpPipeLineInfo("http://test.com");
pipeLine.Add("test", info);
// Act - 존재하지 않는 키 제거 시도
await pipeLine.RemoveAsync("nonexistent");
// Assert - 기존 항목은 여전히 존재해야 함
var infoList = GetInfoListField();
Assert.IsTrue(infoList.ContainsKey("test"));
Assert.AreEqual(info, infoList["test"]);
}
/// <summary>
/// HttpPipeLine의 private infoList 필드 가져오기
/// </summary>
private Dictionary<string, HttpPipeLineInfo> GetInfoListField()
{
var fieldInfo = typeof(HttpPipeLine).GetField("infoList",
BindingFlags.NonPublic | BindingFlags.Instance);
return (Dictionary<string, HttpPipeLineInfo>)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>
[Test]
public async UniTask Excute_WithJObjectResponse_ProcessesDataCorrectly()
{
// Arrange
bool handlerCalled = false;
DataObject? receivedData = null;
var mockResponse = @"{""name"": ""테스트"", ""value"": 123}";
// DataMask와 DataMapper 설정
var dataMask = new DataMask();
dataMask["name"] = "이름";
dataMask["value"] = 0;
var dataMapper = new DataMapper(dataMask);
// HttpPipeLineInfo 설정
var info = new HttpPipeLineInfo("http://test.com")
.setDataMapper(dataMapper)
.setHandler((data) =>
{
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
Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
Assert.IsNotNull(receivedData, "핸들러에 전달된 데이터가 null입니다.");
Assert.AreEqual("테스트", receivedData?.GetString("name"));
Assert.AreEqual(123, receivedData?.GetInt("value"));
}
finally
{
// 설정한 응답 정리
MockHttpRequester.ClearResponses("http://test.com");
await pipeLine.RemoveAsync("testKey");
}
}
/// <summary>
/// Excute 메소드에서 JArray 응답을 처리하는 기능 테스트
/// </summary>
[Test]
public async UniTask Excute_WithJArrayResponse_ProcessesDataCorrectly()
{
// Arrange
bool handlerCalled = false;
IDataObject? receivedData = null;
var mockResponse = @"[{""name"": ""항목1"", ""value"": 10}, {""name"": ""항목2"", ""value"": 20}]";
// 배열용 DataMask 설정
var dataMask = new DataMask
{
["name"] = "이름",
["value"] = 0
};
var dataMapper = new DataMapper(dataMask);
// HttpPipeLineInfo 설정
var info = new HttpPipeLineInfo("http://test.com")
.setDataMapper(dataMapper)
.setHandler((data) =>
{
handlerCalled = true;
receivedData = data;
});
pipeLine.Add("testArrayKey", info);
try
{
// MockHttpRequester에 테스트용 응답 설정
MockHttpRequester.SetResponse("http://test.com", mockResponse);
// Act
await pipeLine.Excute("testArrayKey");
// Assert
Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
Assert.IsNotNull(receivedData, "핸들러에 전달된 데이터가 null입니다.");
Assert.IsTrue(receivedData is DataArray, "결과가 DataArray가 아닙니다.");
var dataArray = receivedData as DataArray;
Assert.AreEqual(2, dataArray?.Count);
Assert.AreEqual("항목1", dataArray?[0].GetString("name"));
Assert.AreEqual(10, dataArray?[0].GetInt("value"));
Assert.AreEqual("항목2", dataArray?[1].GetString("name"));
Assert.AreEqual(20, dataArray?[1].GetInt("value"));
}
finally
{
// 설정한 응답 정리
MockHttpRequester.ClearResponses("http://test.com");
await pipeLine.RemoveAsync("testArrayKey");
}
}
/// <summary>
/// Excute 메소드에서 존재하지 않는 키를 호출했을 때의 동작 테스트
/// </summary>
[Test]
public void Excute_WithNonExistingKey_DoesNothing()
{
// Arrange - 의도적으로 아무 것도 설정하지 않음
// Act & Assert - 예외가 발생하지 않아야 함
Assert.DoesNotThrow(() => pipeLine.Excute("nonExistingKey"));
}
/// <summary>
/// UVC.Tests.MockHttpRequester를 사용하여 AGV 데이터 파싱 테스트
/// </summary>
[Test]
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);
// HttpPipeLineInfo 설정
var info = new HttpPipeLineInfo(agvUrl, "get")
.setDataMapper(dataMapper)
.setHandler((data) =>
{
handlerCalled = true;
receivedData = data;
});
pipeLine.Add("agvData", info);
// Act
await pipeLine.Excute("agvData");
// Assert
Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
Assert.IsNotNull(receivedData, "데이터가 null입니다.");
Assert.IsTrue(receivedData is DataArray, "결과가 DataArray가 아닙니다.");
var dataArray = receivedData as DataArray;
Assert.IsTrue(dataArray?.Count > 0, "데이터 배열이 비어 있습니다.");
// 첫 번째 항목 확인
Assert.IsNotNull(dataArray?[0].GetString("VHL_NAME"));
Assert.IsNotNull(dataArray?[0].GetString("AGV_IDX"));
Assert.IsNotNull(dataArray?[0].GetString("B_INSTALL"));
}
/// <summary>
/// UVC.Tests.MockHttpRequester를 사용하여 알람 데이터 파싱 테스트
/// </summary>
[Test]
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);
// HttpPipeLineInfo 설정
var info = new HttpPipeLineInfo(alarmUrl, "get")
.setDataMapper(dataMapper)
.setHandler((data) =>
{
handlerCalled = true;
receivedData = data;
});
pipeLine.Add("alarmData", info);
try
{
// Act
await pipeLine.Excute("alarmData");
// Assert
Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
Assert.IsNotNull(receivedData, "데이터가 null입니다.");
Assert.IsTrue(receivedData is DataArray, "결과가 DataArray가 아닙니다.");
var dataArray = receivedData as DataArray;
Assert.IsTrue(dataArray?.Count > 0, "데이터 배열이 비어 있습니다.");
// 첫 번째 항목 확인
Assert.IsNotNull(dataArray?[0].GetString("ID"));
Assert.IsNotNull(dataArray?[0].GetString("ALARM_TYPE"));
Assert.IsNotNull(dataArray?[0].GetString("LEVEL"));
Assert.IsNotNull(dataArray?[0].GetString("MESSAGE"));
Assert.IsNotNull(dataArray?[0].GetString("CODE"));
Assert.IsNotNull(dataArray?[0].GetString("ICON"));
Assert.IsNotNull(dataArray?[0].GetString("SET_TIME"));
}
finally
{
await pipeLine.RemoveAsync("alarmData");
}
}
/// <summary>
/// UVC.Tests.MockHttpRequester를 사용하여 여러 URL 유형에 대한 동시 테스트
/// </summary>
[Test]
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"] = "알람타입" }
}
}
};
// 각 데이터 타입별 HttpPipeLineInfo 설정 및 등록
foreach (var item in urls)
{
string key = item.Key;
var info = new HttpPipeLineInfo(item.Value, "get")
.setDataMapper(new DataMapper(dataMasks[key]))
.setHandler((data) =>
{
handlerCallCount++;
results[key] = data;
});
pipeLine.Add(key, info);
await pipeLine.Excute(key);
}
// Assert
Assert.AreEqual(urls.Count, handlerCallCount, "모든 핸들러가 호출되지 않았습니다.");
Assert.AreEqual(urls.Count, results.Count, "모든 결과가 수집되지 않았습니다.");
// 각 데이터 타입별 결과 확인
foreach (var result in results)
{
Assert.IsNotNull(result.Value, $"{result.Key} 데이터가 null입니다.");
Assert.IsTrue(result.Value is DataArray, $"{result.Key} 결과가 DataArray가 아닙니다.");
var dataArray = result.Value as DataArray;
Assert.IsTrue(dataArray?.Count > 0, $"{result.Key} 데이터 배열이 비어 있습니다.");
}
}
/// <summary>
/// UVC.Tests.MockHttpRequester를 사용한 운반대(Carrier) 데이터 테스트
/// </summary>
[Test]
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);
// HttpPipeLineInfo 설정
var info = new HttpPipeLineInfo(testUrl, "get")
.setDataMapper(dataMapper)
.setHandler((data) =>
{
handlerCalled = true;
receivedData = data;
});
pipeLine.Add("testCarrierData", info);
// Act
await pipeLine.Excute("testCarrierData");
// Assert
Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
Assert.IsNotNull(receivedData, "응답 데이터가 null입니다.");
Assert.IsTrue(receivedData is DataArray, "응답이 DataArray가 아닙니다.");
var dataArray = receivedData as DataArray;
Assert.IsTrue(dataArray?.Count > 0, "데이터 배열이 비어있습니다.");
// 첫 번째 캐리어 데이터 확인
Assert.IsNotNull(dataArray?[0].GetString("MAIN_CARR_ID"), "캐리어ID가 null입니다.");
Assert.IsNotNull(dataArray?[0].GetString("SUB_CARR_ID"), "서브ID가 null입니다.");
Assert.IsNotNull(dataArray?[0].GetString("CARR_SEQ"), "순번이 null입니다.");
Assert.IsNotNull(dataArray?[0].GetString("CARR_USE"), "사용상태가 null입니다.");
}
/// <summary>
/// UVC.Tests.MockHttpRequester를 사용하여 BaseInfo 데이터 파싱 테스트
/// </summary>
[Test]
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);
// HttpPipeLineInfo 설정
var info = new HttpPipeLineInfo(baseInfoUrl, "get")
.setDataMapper(dataMapper)
.setHandler((data) =>
{
handlerCalled = true;
receivedData = data;
});
pipeLine.Add("baseInfoData", info);
try
{
// Act
await pipeLine.Excute("baseInfoData");
// Assert
Assert.IsTrue(handlerCalled, "핸들러가 호출되지 않았습니다.");
Assert.IsNotNull(receivedData, "데이터가 null입니다.");
Assert.IsTrue(receivedData is DataObject, "결과가 DataObject가 아닙니다.");
var dataObject = receivedData as DataObject;
// AGV 데이터 검증
Assert.IsNotNull(dataObject?.Get("AGV"), "AGV 데이터가 없습니다.");
var agvData = dataObject?.Get("AGV") as DataArray;
Assert.IsTrue(agvData?.Count > 0, "AGV 데이터 배열이 비어 있습니다.");
Assert.IsNotNull(agvData?[0].GetString("VHL_NAME"), "VHL_NAME 필드가 없습니다.");
Assert.IsNotNull(agvData?[0].GetString("AGV_IDX"), "AGV_IDX 필드가 없습니다.");
Assert.IsNotNull(agvData?[0].GetString("B_INSTALL"), "B_INSTALL 필드가 없습니다.");
// EQUIPMENT 데이터 검증
Assert.IsNotNull(dataObject?.Get("EQUIPMENT"), "EQUIPMENT 데이터가 없습니다.");
var equipmentData = dataObject?.Get("EQUIPMENT") as DataArray;
Assert.IsTrue(equipmentData?.Count > 0, "EQUIPMENT 데이터 배열이 비어 있습니다.");
Assert.IsNotNull(equipmentData?[0].GetString("EQP_ID"), "EQP_ID 필드가 없습니다.");
Assert.IsNotNull(equipmentData?[0].GetString("KOR_EQP_NAME"), "KOR_EQP_NAME 필드가 없습니다.");
Assert.IsNotNull(equipmentData?[0].GetString("STATE_ID"), "STATE_ID 필드가 없습니다.");
// ALARM 데이터 검증
Assert.IsNotNull(dataObject?.Get("ALARM"), "ALARM 데이터가 없습니다.");
var alarmData = dataObject?.Get("ALARM") as DataArray;
Assert.IsTrue(alarmData?.Count > 0, "ALARM 데이터 배열이 비어 있습니다.");
Assert.IsNotNull(alarmData?[0].GetString("ID"), "ID 필드가 없습니다.");
Assert.IsNotNull(alarmData?[0].GetString("ALARM_TYPE"), "ALARM_TYPE 필드가 없습니다.");
Assert.IsNotNull(alarmData?[0].GetString("LEVEL"), "LEVEL 필드가 없습니다.");
Assert.IsNotNull(alarmData?[0].GetString("MESSAGE"), "MESSAGE 필드가 없습니다.");
}
finally
{
await pipeLine.RemoveAsync("baseInfoData");
}
}
/// <summary>
/// 반복 실행 기능을 테스트합니다.
/// </summary>
[Test]
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[]
{
@"{""id"": 1, ""status"": ""pending"", ""timestamp"": ""2025-06-09T10:00:00Z""}",
@"{""id"": 1, ""status"": ""processing"", ""timestamp"": ""2025-06-09T10:00:10Z""}",
@"{""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);
// 반복 실행 설정을 포함한 HttpPipeLineInfo 생성
var info = new HttpPipeLineInfo(testUrl, "get")
.setDataMapper(dataMapper)
.setHandler(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
Assert.AreEqual(expectedCallCount, handlerCallCount, "핸들러 호출 횟수가 예상과 다릅니다");
// 응답이 순차적으로 처리되었는지 확인
Assert.AreEqual("pending", receivedResponses[0], "첫 번째 응답이 올바르지 않습니다");
Assert.AreEqual("processing", receivedResponses[1], "두 번째 응답이 올바르지 않습니다");
Assert.AreEqual("completed", receivedResponses[2], "세 번째 응답이 올바르지 않습니다");
}
finally
{
// 테스트 정리
await pipeLine.RemoveAsync("repeatTest");
MockHttpRequester.ClearResponses(testUrl);
}
}
/// <summary>
/// 실행 중인 반복 요청을 중지하는 기능을 테스트합니다.
/// </summary>
[Test]
public async UniTask Test_StopRepeat_StopsExecutionCorrectly()
{
// Arrange
int handlerCallCount = 0;
string testUrl = "http://test.com/repeat-stop";
int repeatInterval = 100;
// Mock 응답 설정
string mockResponse = @"{""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);
// 무한 반복 설정을 포함한 HttpPipeLineInfo 생성
var info = new HttpPipeLineInfo(testUrl, "get")
.setDataMapper(dataMapper)
.setHandler((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
Assert.Greater(callCountBeforeStop, 0, "반복 호출이 발생하지 않았습니다");
Assert.AreEqual(callCountBeforeStop, handlerCallCount, "반복 중지 후에도 호출이 계속 발생했습니다");
}
finally
{
// 테스트 정리
await pipeLine.RemoveAsync("infiniteRepeatTest");
MockHttpRequester.ClearResponses(testUrl);
}
}
/// <summary>
/// 여러 개의 반복 요청을 동시에 실행하고 개별적으로 중지하는 기능을 테스트합니다.
/// </summary>
[Test]
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 = @"{""id"": 3, ""name"": ""작업1""}";
string mockResponse2 = @"{""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 HttpPipeLineInfo(testUrl1, "get")
.setDataMapper(dataMapper)
.setHandler((data) => { handlerCallCount1++; })
.setRepeat(true, 0, repeatInterval1, false);
var info2 = new HttpPipeLineInfo(testUrl2, "get")
.setDataMapper(dataMapper)
.setHandler((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
Assert.Greater(callCount1AfterStop, 0, "첫 번째 반복 요청 실행이 발생하지 않았습니다");
Assert.Greater(handlerCallCount2, callCount2BeforeSecondStop, "두 번째 반복이 계속 실행되지 않았습니다");
Assert.AreEqual(callCount1AfterStop, handlerCallCount1, "첫 번째 반복이 중지되지 않았습니다");
// 두 번째 반복도 중지
await pipeLine.StopRepeat("repeatTest2");
// 호출 횟수 다시 기록
int callCount2AfterStop = handlerCallCount2;
// 더 이상 호출이 없는지 확인하기 위해 대기
await UniTask.Delay(Math.Max(repeatInterval1, repeatInterval2) * 2 + 100 * 2 + 50);
// 모든 호출이 중지되었는지 확인
Assert.AreEqual(callCount1AfterStop, handlerCallCount1, "첫 번째 반복이 계속되었습니다");
Assert.AreEqual(callCount2AfterStop, handlerCallCount2, "두 번째 반복이 계속되었습니다");
}
finally
{
// 테스트 정리
await pipeLine.RemoveAsync("repeatTest1");
await pipeLine.RemoveAsync("repeatTest2");
MockHttpRequester.ClearResponses(testUrl1);
MockHttpRequester.ClearResponses(testUrl2);
}
}
/// <summary>
/// 지정된 횟수만큼 반복 실행 후 자동 중단되는 기능을 테스트합니다.
/// </summary>
[Test]
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 = @"{""id"": 5, ""message"": ""자동 중단 테스트""}";
MockHttpRequester.SetResponse(testUrl, mockResponse);
// DataMask 설정
var dataMask = new DataMask();
dataMask["id"] = 0;
dataMask["message"] = "";
var dataMapper = new DataMapper(dataMask);
// 반복 횟수가 지정된 HttpPipeLineInfo 생성
var info = new HttpPipeLineInfo(testUrl, "get")
.setDataMapper(dataMapper)
.setHandler((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
Assert.AreEqual(repeatCount, handlerCallCount, "지정된 횟수만큼 반복 실행되지 않았습니다");
Assert.AreEqual(repeatCount, receivedData.Count, "기대한 데이터 수와 다릅니다");
// 각 응답이 올바르게 처리되었는지 확인
var data = receivedData[0];
Assert.IsNotNull(data, "데이터가 null입니다");
var dataObject = data as DataObject;
Assert.AreEqual(5, dataObject?.GetInt("id"), "ID가 올바르지 않습니다");
Assert.AreEqual("자동 중단 테스트", dataObject?.GetString("message"), "메시지가 올바르지 않습니다");
// 자동으로 제거되었는지 확인
var repeatTokenSources = GetRepeatTokenSourcesField();
Assert.IsFalse(repeatTokenSources.ContainsKey("countedRepeatTest"),
"실행 완료 후 반복 토큰이 제거되지 않았습니다");
}
finally
{
// 테스트 정리
await pipeLine.RemoveAsync("countedRepeatTest");
MockHttpRequester.ClearResponses(testUrl);
}
}
/// <summary>
/// HttpPipeLine의 private repeatTokenSources 필드 가져오기
/// </summary>
private Dictionary<string, CancellationTokenSource> GetRepeatTokenSourcesField()
{
var fieldInfo = typeof(HttpPipeLine).GetField("repeatTokenSources",
BindingFlags.NonPublic | BindingFlags.Instance);
return (Dictionary<string, CancellationTokenSource>)fieldInfo.GetValue(pipeLine);
}
}
}