28 01 25
Добавил запрос поиска пользователей по начаоу логина. И тест добавил. Все тесты проходят.
This commit is contained in:
parent
ebf7c9f18e
commit
22fb35d1d4
@ -31,6 +31,10 @@ import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_AddUser_Re
|
|||||||
import server.logic.ws_protocol.JSON.handlers.tempToTest.Net_GetUser_Handler;
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.Net_GetUser_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_GetUser_Request;
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_GetUser_Request;
|
||||||
|
|
||||||
|
// --- NEW: SearchUsers ---
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.Net_SearchUsers_Handler;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_SearchUsers_Request;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.handlers.userParams.Net_GetUserParam_Handler;
|
import server.logic.ws_protocol.JSON.handlers.userParams.Net_GetUserParam_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.userParams.Net_ListUserParams_Handler;
|
import server.logic.ws_protocol.JSON.handlers.userParams.Net_ListUserParams_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.userParams.Net_UpsertUserParam_Handler;
|
import server.logic.ws_protocol.JSON.handlers.userParams.Net_UpsertUserParam_Handler;
|
||||||
@ -54,6 +58,7 @@ public final class JsonHandlerRegistry {
|
|||||||
private static final Map<String, JsonMessageHandler> HANDLERS = Map.ofEntries(
|
private static final Map<String, JsonMessageHandler> HANDLERS = Map.ofEntries(
|
||||||
Map.entry("AddUser", new Net_AddUser_Handler()),
|
Map.entry("AddUser", new Net_AddUser_Handler()),
|
||||||
Map.entry("GetUser", new Net_GetUser_Handler()),
|
Map.entry("GetUser", new Net_GetUser_Handler()),
|
||||||
|
Map.entry("SearchUsers", new Net_SearchUsers_Handler()),
|
||||||
|
|
||||||
// --- auth ---
|
// --- auth ---
|
||||||
Map.entry("AuthChallenge", new Net_AuthChallenge_Handler()),
|
Map.entry("AuthChallenge", new Net_AuthChallenge_Handler()),
|
||||||
@ -80,6 +85,7 @@ public final class JsonHandlerRegistry {
|
|||||||
private static final Map<String, Class<? extends Net_Request>> REQUEST_TYPES = Map.ofEntries(
|
private static final Map<String, Class<? extends Net_Request>> REQUEST_TYPES = Map.ofEntries(
|
||||||
Map.entry("AddUser", Net_AddUser_Request.class),
|
Map.entry("AddUser", Net_AddUser_Request.class),
|
||||||
Map.entry("GetUser", Net_GetUser_Request.class),
|
Map.entry("GetUser", Net_GetUser_Request.class),
|
||||||
|
Map.entry("SearchUsers", Net_SearchUsers_Request.class),
|
||||||
|
|
||||||
// --- auth ---
|
// --- auth ---
|
||||||
Map.entry("AuthChallenge", Net_AuthChallenge_Request.class),
|
Map.entry("AuthChallenge", Net_AuthChallenge_Request.class),
|
||||||
|
|||||||
@ -0,0 +1,77 @@
|
|||||||
|
package server.logic.ws_protocol.JSON.handlers.tempToTest;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_SearchUsers_Request;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_SearchUsers_Response;
|
||||||
|
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||||
|
import server.logic.ws_protocol.WireCodes;
|
||||||
|
import shine.db.dao.SolanaUsersDAO;
|
||||||
|
import shine.db.entities.SolanaUserEntry;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Net_SearchUsers_Handler implements JsonMessageHandler {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Net_SearchUsers_Handler.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
|
||||||
|
Net_SearchUsers_Request req = (Net_SearchUsers_Request) baseRequest;
|
||||||
|
|
||||||
|
if (req.getPrefix() == null || req.getPrefix().isBlank()) {
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.BAD_REQUEST,
|
||||||
|
"BAD_FIELDS",
|
||||||
|
"Некорректные поля: prefix"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String prefix = req.getPrefix().trim();
|
||||||
|
|
||||||
|
try {
|
||||||
|
SolanaUsersDAO dao = SolanaUsersDAO.getInstance();
|
||||||
|
List<SolanaUserEntry> users = dao.searchByLoginPrefix(prefix); // case-insensitive + LIMIT 5
|
||||||
|
|
||||||
|
List<String> logins = new ArrayList<>();
|
||||||
|
for (SolanaUserEntry u : users) {
|
||||||
|
if (u != null && u.getLogin() != null) {
|
||||||
|
logins.add(u.getLogin()); // регистр как в БД
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Net_SearchUsers_Response resp = new Net_SearchUsers_Response();
|
||||||
|
resp.setOp(req.getOp());
|
||||||
|
resp.setRequestId(req.getRequestId());
|
||||||
|
resp.setStatus(WireCodes.Status.OK);
|
||||||
|
resp.setLogins(logins);
|
||||||
|
|
||||||
|
log.info("✅ SearchUsers ok: prefix='{}' -> {}", prefix, logins.size());
|
||||||
|
return resp;
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.error("❌ DB error SearchUsers", e);
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.SERVER_DATA_ERROR,
|
||||||
|
"DB_ERROR",
|
||||||
|
"Ошибка БД"
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ Internal error SearchUsers", e);
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.INTERNAL_ERROR,
|
||||||
|
"INTERNAL_ERROR",
|
||||||
|
"Внутренняя ошибка сервера"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package server.logic.ws_protocol.JSON.handlers.tempToTest.entyties;
|
||||||
|
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запрос SearchUsers — поиск логинов по префиксу.
|
||||||
|
*
|
||||||
|
* Клиент отправляет:
|
||||||
|
* {
|
||||||
|
* "op": "SearchUsers",
|
||||||
|
* "requestId": "su-1",
|
||||||
|
* "payload": { "prefix": "any" }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Поиск по prefix выполняется без учёта регистра.
|
||||||
|
* В ответе возвращаем логины с тем регистром, как в БД.
|
||||||
|
*/
|
||||||
|
public class Net_SearchUsers_Request extends Net_Request {
|
||||||
|
|
||||||
|
private String prefix;
|
||||||
|
|
||||||
|
public String getPrefix() { return prefix; }
|
||||||
|
public void setPrefix(String prefix) { this.prefix = prefix; }
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package server.logic.ws_protocol.JSON.handlers.tempToTest.entyties;
|
||||||
|
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ответ SearchUsers.
|
||||||
|
*
|
||||||
|
* Всегда status=200.
|
||||||
|
*
|
||||||
|
* Пример:
|
||||||
|
* {
|
||||||
|
* "op": "SearchUsers",
|
||||||
|
* "requestId": "su-1",
|
||||||
|
* "status": 200,
|
||||||
|
* "payload": {
|
||||||
|
* "logins": ["Anya", "andrew", "Angel"]
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
public class Net_SearchUsers_Response extends Net_Response {
|
||||||
|
|
||||||
|
private List<String> logins = new ArrayList<>();
|
||||||
|
|
||||||
|
public List<String> getLogins() { return logins; }
|
||||||
|
public void setLogins(List<String> logins) { this.logins = logins; }
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ import test.it.utils.log.TestResult;
|
|||||||
import test.it.utils.ws.WsSession;
|
import test.it.utils.ws.WsSession;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||||||
* - теперь AddUser может вернуть 409 не только USER_ALREADY_EXISTS,
|
* - теперь AddUser может вернуть 409 не только USER_ALREADY_EXISTS,
|
||||||
* но и BLOCKCHAIN_ALREADY_EXISTS / BLOCKCHAIN_STATE_ALREADY_EXISTS.
|
* но и BLOCKCHAIN_ALREADY_EXISTS / BLOCKCHAIN_STATE_ALREADY_EXISTS.
|
||||||
* - дополнительно проверяем GetUser (status=200 всегда).
|
* - дополнительно проверяем GetUser (status=200 всегда).
|
||||||
|
* - добавлен SearchUsers: поиск по префиксу (первые 3 символа).
|
||||||
*/
|
*/
|
||||||
public class IT_01_AddUser {
|
public class IT_01_AddUser {
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ public class IT_01_AddUser {
|
|||||||
checkAddUser200or409(r, resp3);
|
checkAddUser200or409(r, resp3);
|
||||||
checkGetUserMustExist(r, ws, TestConfig.LOGIN3(), t);
|
checkGetUserMustExist(r, ws, TestConfig.LOGIN3(), t);
|
||||||
|
|
||||||
// Доп: проверяем case-insensitive поиск
|
// Доп: проверяем case-insensitive поиск в GetUser
|
||||||
String mixed = mixCase(TestConfig.LOGIN());
|
String mixed = mixCase(TestConfig.LOGIN());
|
||||||
r.ok("GetUser case-insensitive: запрос=" + mixed + " (должен найти " + TestConfig.LOGIN() + ")");
|
r.ok("GetUser case-insensitive: запрос=" + mixed + " (должен найти " + TestConfig.LOGIN() + ")");
|
||||||
checkGetUserMustExist(r, ws, mixed, t);
|
checkGetUserMustExist(r, ws, mixed, t);
|
||||||
@ -58,6 +60,12 @@ public class IT_01_AddUser {
|
|||||||
r.ok("GetUser missing: " + missing);
|
r.ok("GetUser missing: " + missing);
|
||||||
checkGetUserMustNotExist(r, ws, missing, t);
|
checkGetUserMustNotExist(r, ws, missing, t);
|
||||||
|
|
||||||
|
// SearchUsers: один раз ищем по первым трём символам логина USER1
|
||||||
|
String prefix3 = first3(TestConfig.LOGIN());
|
||||||
|
String prefix3Mixed = mixCase(prefix3);
|
||||||
|
r.ok("SearchUsers: prefix(3)='" + prefix3Mixed + "' (должен вернуть список и содержать " + TestConfig.LOGIN() + ")");
|
||||||
|
checkSearchUsersMustContain(r, ws, prefix3Mixed, TestConfig.LOGIN(), t);
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
r.fail("IT_01_AddUser упал: " + e.getMessage());
|
r.fail("IT_01_AddUser упал: " + e.getMessage());
|
||||||
}
|
}
|
||||||
@ -183,6 +191,37 @@ public class IT_01_AddUser {
|
|||||||
r.ok("GetUser: exists=false (ok)");
|
r.ok("GetUser: exists=false (ok)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void checkSearchUsersMustContain(TestResult r, WsSession ws, String prefix, String expectedLogin, Duration t) {
|
||||||
|
String resp = ws.call("SearchUsers#" + prefix, JsonBuilders.searchUsers(prefix), t);
|
||||||
|
|
||||||
|
int st = JsonParsers.status(resp);
|
||||||
|
if (st != 200) {
|
||||||
|
r.fail("SearchUsers: ожидали status=200, получили " + st + ", resp=" + resp);
|
||||||
|
fail("SearchUsers unexpected status=" + st);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> logins = JsonParsers.searchLogins(resp);
|
||||||
|
if (logins == null || logins.isEmpty()) {
|
||||||
|
r.fail("SearchUsers: ожидали непустой список, resp=" + resp);
|
||||||
|
fail("SearchUsers expected non-empty list");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ВАЖНО: ожидаемый логин должен быть в ответе в регистре БД (каноничный expectedLogin)
|
||||||
|
boolean found = false;
|
||||||
|
for (String s : logins) {
|
||||||
|
if (expectedLogin.equals(s)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
r.fail("SearchUsers: ожидаемый логин не найден. expected=" + expectedLogin + ", got=" + logins + ", resp=" + resp);
|
||||||
|
fail("SearchUsers expected login not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
r.ok("SearchUsers: ok, prefix=" + prefix + ", results=" + logins.size() + ", contains=" + expectedLogin);
|
||||||
|
}
|
||||||
|
|
||||||
private static String canonicalLogin(String anyCaseLogin) {
|
private static String canonicalLogin(String anyCaseLogin) {
|
||||||
if (anyCaseLogin == null) return null;
|
if (anyCaseLogin == null) return null;
|
||||||
String x = anyCaseLogin.trim();
|
String x = anyCaseLogin.trim();
|
||||||
@ -204,6 +243,13 @@ public class IT_01_AddUser {
|
|||||||
return Character.toUpperCase(x.charAt(0)) + x.substring(1).toLowerCase();
|
return Character.toUpperCase(x.charAt(0)) + x.substring(1).toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String first3(String s) {
|
||||||
|
if (s == null) return "";
|
||||||
|
String x = s.trim();
|
||||||
|
if (x.length() <= 3) return x;
|
||||||
|
return x.substring(0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isBlank(String s) {
|
private static boolean isBlank(String s) {
|
||||||
return s == null || s.trim().isEmpty();
|
return s == null || s.trim().isEmpty();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,6 +60,21 @@ public final class JsonBuilders {
|
|||||||
""".formatted(requestId, login);
|
""".formatted(requestId, login);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------- SearchUsers ----------------
|
||||||
|
|
||||||
|
public static String searchUsers(String prefix) {
|
||||||
|
String requestId = TestIds.next("searchusers");
|
||||||
|
return """
|
||||||
|
{
|
||||||
|
"op": "SearchUsers",
|
||||||
|
"requestId": "%s",
|
||||||
|
"payload": {
|
||||||
|
"prefix": "%s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".formatted(requestId, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------- AuthChallenge ----------------
|
// ---------------- AuthChallenge ----------------
|
||||||
|
|
||||||
public static String authChallenge(String login) {
|
public static String authChallenge(String login) {
|
||||||
|
|||||||
@ -147,6 +147,25 @@ public final class JsonParsers {
|
|||||||
return getPayloadText(json, "deviceKey");
|
return getPayloadText(json, "deviceKey");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------- SearchUsers helpers ----------------
|
||||||
|
|
||||||
|
public static List<String> searchLogins(String json) {
|
||||||
|
List<String> res = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
JsonNode root = MAPPER.readTree(json);
|
||||||
|
JsonNode payload = root.get("payload");
|
||||||
|
if (payload == null) return res;
|
||||||
|
|
||||||
|
JsonNode arr = payload.get("logins");
|
||||||
|
if (arr == null || !arr.isArray()) return res;
|
||||||
|
|
||||||
|
for (JsonNode x : arr) {
|
||||||
|
if (x != null && !x.isNull()) res.add(x.asText());
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
private static String getPayloadText(String json, String field) {
|
private static String getPayloadText(String json, String field) {
|
||||||
try {
|
try {
|
||||||
JsonNode root = MAPPER.readTree(json);
|
JsonNode root = MAPPER.readTree(json);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user