Tests 3 restructure (#754)
* moved files * moved files * initial v3 additions * moves req data * tests passing, restructuring, remove v2 * fmt; clippy; prepare * merge conflicts + issues * merge conflict, fmt, clippy, prepare * revs * fixed failing test * fixed tests
This commit is contained in:
parent
f4880d0519
commit
74973e73e6
@ -1,126 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT m.id id, m.title title, m.description description, m.color color,\n m.icon_url icon_url, m.slug slug,\n pt.name project_type, u.username username, u.avatar_url avatar_url,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null) categories,\n ARRAY_AGG(DISTINCT lo.loader) filter (where lo.loader is not null) loaders,\n ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,\n ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games,\n ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is false) gallery,\n ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is true) featured_gallery,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'field_id', vf.field_id,\n 'int_value', vf.int_value,\n 'enum_value', vf.enum_value,\n 'string_value', vf.string_value\n )\n ) filter (where vf.field_id is not null) version_fields,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'lf_id', lf.id,\n 'loader_name', lo.loader,\n 'field', lf.field,\n 'field_type', lf.field_type,\n 'enum_type', lf.enum_type,\n 'min_val', lf.min_val,\n 'max_val', lf.max_val,\n 'optional', lf.optional\n )\n ) filter (where lf.id is not null) loader_fields,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'id', lfev.id,\n 'enum_id', lfev.enum_id,\n 'value', lfev.value,\n 'ordering', lfev.ordering,\n 'created', lfev.created,\n 'metadata', lfev.metadata\n ) \n ) filter (where lfev.id is not null) loader_field_enum_values\n FROM mods m\n LEFT OUTER JOIN mods_categories mc ON joining_mod_id = m.id AND mc.is_additional = FALSE\n LEFT OUTER JOIN categories c ON mc.joining_category_id = c.id\n LEFT OUTER JOIN versions v ON v.mod_id = m.id AND v.status != ALL($2)\n LEFT OUTER JOIN loaders_versions lv ON lv.version_id = v.id\n LEFT OUTER JOIN loaders lo ON lo.id = lv.loader_id\n LEFT JOIN loaders_project_types lpt ON lpt.joining_loader_id = lo.id\n LEFT JOIN project_types pt ON pt.id = lpt.joining_project_type_id\n LEFT JOIN loaders_project_types_games lptg ON lptg.loader_id = lo.id AND lptg.project_type_id = pt.id\n LEFT JOIN games g ON lptg.game_id = g.id\n LEFT OUTER JOIN mods_gallery mg ON mg.mod_id = m.id\n INNER JOIN team_members tm ON tm.team_id = m.team_id AND tm.role = $3 AND tm.accepted = TRUE\n INNER JOIN users u ON tm.user_id = u.id\n LEFT OUTER JOIN version_fields vf on v.id = vf.version_id\n LEFT OUTER JOIN loader_fields lf on vf.field_id = lf.id\n LEFT OUTER JOIN loader_field_enums lfe on lf.enum_type = lfe.id\n LEFT OUTER JOIN loader_field_enum_values lfev on lfev.enum_id = lfe.id\n WHERE m.id = $1\n GROUP BY m.id, pt.id, u.id;\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "description",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "color",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "slug",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "project_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "avatar_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "categories",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "loaders",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "project_types",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "games",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "gallery",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "featured_gallery",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "version_fields",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "loader_fields",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "loader_field_enum_values",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"TextArray",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "2253e9a36185947199cb5b3909a2ad9944f35b4554d340bddd0ad32782f193b3"
|
||||
}
|
||||
@ -1,180 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT m.id id, v.id version_id, m.title title, m.description description, m.downloads downloads, m.follows follows,\n m.icon_url icon_url, m.published published, m.approved approved, m.updated updated,\n m.team_id team_id, m.license license, m.slug slug, m.status status_name, m.color color,\n pt.name project_type_name, u.username username,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is false) categories,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories,\n ARRAY_AGG(DISTINCT lo.loader) filter (where lo.loader is not null) loaders,\n ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,\n ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games,\n ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is false) gallery,\n ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is true) featured_gallery,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'field_id', vf.field_id,\n 'int_value', vf.int_value,\n 'enum_value', vf.enum_value,\n 'string_value', vf.string_value\n )\n ) filter (where vf.field_id is not null) version_fields,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'lf_id', lf.id,\n 'loader_name', lo.loader,\n 'field', lf.field,\n 'field_type', lf.field_type,\n 'enum_type', lf.enum_type,\n 'min_val', lf.min_val,\n 'max_val', lf.max_val,\n 'optional', lf.optional\n )\n ) filter (where lf.id is not null) loader_fields,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'id', lfev.id,\n 'enum_id', lfev.enum_id,\n 'value', lfev.value,\n 'ordering', lfev.ordering,\n 'created', lfev.created,\n 'metadata', lfev.metadata\n ) \n ) filter (where lfev.id is not null) loader_field_enum_values\n\n FROM versions v\n INNER JOIN mods m ON v.mod_id = m.id AND m.status = ANY($2)\n LEFT OUTER JOIN mods_categories mc ON joining_mod_id = m.id\n LEFT OUTER JOIN categories c ON mc.joining_category_id = c.id\n LEFT OUTER JOIN loaders_versions lv ON lv.version_id = v.id\n LEFT OUTER JOIN loaders lo ON lo.id = lv.loader_id\n LEFT JOIN loaders_project_types lpt ON lpt.joining_loader_id = lo.id\n LEFT JOIN project_types pt ON pt.id = lpt.joining_project_type_id\n LEFT JOIN loaders_project_types_games lptg ON lptg.loader_id = lo.id AND lptg.project_type_id = pt.id\n LEFT JOIN games g ON lptg.game_id = g.id\n LEFT OUTER JOIN mods_gallery mg ON mg.mod_id = m.id\n INNER JOIN team_members tm ON tm.team_id = m.team_id AND tm.role = $3 AND tm.accepted = TRUE\n INNER JOIN users u ON tm.user_id = u.id\n LEFT OUTER JOIN version_fields vf on v.id = vf.version_id\n LEFT OUTER JOIN loader_fields lf on vf.field_id = lf.id\n LEFT OUTER JOIN loader_field_enums lfe on lf.enum_type = lfe.id\n LEFT OUTER JOIN loader_field_enum_values lfev on lfev.enum_id = lfe.id\n WHERE v.status != ANY($1)\n GROUP BY v.id, m.id, pt.id, u.id;\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "version_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "downloads",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "follows",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "published",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "approved",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "updated",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "team_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "license",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "slug",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "status_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "color",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "project_type_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "categories",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "additional_categories",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"name": "loaders",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"name": "project_types",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"name": "games",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"name": "gallery",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"name": "featured_gallery",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"name": "version_fields",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"name": "loader_fields",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"name": "loader_field_enum_values",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray",
|
||||
"TextArray",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "2ac81625d4facd7bbe14a682f0c89a6c6fbaa555e112db3adf7accace9b55df5"
|
||||
}
|
||||
120
.sqlx/query-a87e276d202a517951a50183adb24306e9fcae87e6eac458e7f813ebb57c78c4.json
generated
Normal file
120
.sqlx/query-a87e276d202a517951a50183adb24306e9fcae87e6eac458e7f813ebb57c78c4.json
generated
Normal file
@ -0,0 +1,120 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT m.id id, m.title title, m.description description, m.color color,\n m.icon_url icon_url, m.slug slug,\n u.username username, u.avatar_url avatar_url,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null) categories,\n ARRAY_AGG(DISTINCT lo.loader) filter (where lo.loader is not null) loaders,\n ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,\n ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games,\n ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is false) gallery,\n ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is true) featured_gallery,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'field_id', vf.field_id,\n 'int_value', vf.int_value,\n 'enum_value', vf.enum_value,\n 'string_value', vf.string_value\n )\n ) filter (where vf.field_id is not null) version_fields,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'lf_id', lf.id,\n 'loader_name', lo.loader,\n 'field', lf.field,\n 'field_type', lf.field_type,\n 'enum_type', lf.enum_type,\n 'min_val', lf.min_val,\n 'max_val', lf.max_val,\n 'optional', lf.optional\n )\n ) filter (where lf.id is not null) loader_fields,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'id', lfev.id,\n 'enum_id', lfev.enum_id,\n 'value', lfev.value,\n 'ordering', lfev.ordering,\n 'created', lfev.created,\n 'metadata', lfev.metadata\n ) \n ) filter (where lfev.id is not null) loader_field_enum_values\n FROM mods m\n LEFT OUTER JOIN mods_categories mc ON joining_mod_id = m.id AND mc.is_additional = FALSE\n LEFT OUTER JOIN categories c ON mc.joining_category_id = c.id\n LEFT OUTER JOIN versions v ON v.mod_id = m.id AND v.status != ALL($2)\n LEFT OUTER JOIN loaders_versions lv ON lv.version_id = v.id\n LEFT OUTER JOIN loaders lo ON lo.id = lv.loader_id\n LEFT JOIN loaders_project_types lpt ON lpt.joining_loader_id = lo.id\n LEFT JOIN project_types pt ON pt.id = lpt.joining_project_type_id\n LEFT JOIN loaders_project_types_games lptg ON lptg.loader_id = lo.id AND lptg.project_type_id = pt.id\n LEFT JOIN games g ON lptg.game_id = g.id\n LEFT OUTER JOIN mods_gallery mg ON mg.mod_id = m.id\n INNER JOIN team_members tm ON tm.team_id = m.team_id AND tm.role = $3 AND tm.accepted = TRUE\n INNER JOIN users u ON tm.user_id = u.id\n LEFT OUTER JOIN version_fields vf on v.id = vf.version_id\n LEFT OUTER JOIN loader_fields lf on vf.field_id = lf.id\n LEFT OUTER JOIN loader_field_enums lfe on lf.enum_type = lfe.id\n LEFT OUTER JOIN loader_field_enum_values lfev on lfev.enum_id = lfe.id\n WHERE m.id = $1\n GROUP BY m.id, u.id;\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "description",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "color",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "slug",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "avatar_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "categories",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "loaders",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "project_types",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "games",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "gallery",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "featured_gallery",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "version_fields",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "loader_fields",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "loader_field_enum_values",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"TextArray",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "a87e276d202a517951a50183adb24306e9fcae87e6eac458e7f813ebb57c78c4"
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT lf.id, lf.field, lf.field_type, lf.optional, lf.min_val, lf.max_val, lf.enum_type\n FROM loader_fields lf\n ",
|
||||
"query": "\n SELECT DISTINCT lf.id, lf.field, lf.field_type, lf.optional, lf.min_val, lf.max_val, lf.enum_type, lfl.loader_id\n FROM loader_fields lf\n LEFT JOIN loader_fields_loaders lfl ON lfl.loader_field_id = lf.id\n WHERE lfl.loader_id = ANY($1)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@ -37,10 +37,17 @@
|
||||
"ordinal": 6,
|
||||
"name": "enum_type",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "loader_id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
"Left": [
|
||||
"Int4Array"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
@ -49,8 +56,9 @@
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "622496d06b9d1e5019d7dcb45ac768558305f1270c1c43ef767f54b9baf5b5af"
|
||||
"hash": "bb6afad07ebfa3b92399bb07aa9e15fa69bd328f44b4bf991e80f6b91fcd3a50"
|
||||
}
|
||||
174
.sqlx/query-d622e6108a96a13d254a489047c7760e1acfa9c41e157f6d75d2538abdba5e4e.json
generated
Normal file
174
.sqlx/query-d622e6108a96a13d254a489047c7760e1acfa9c41e157f6d75d2538abdba5e4e.json
generated
Normal file
@ -0,0 +1,174 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT m.id id, v.id version_id, m.title title, m.description description, m.downloads downloads, m.follows follows,\n m.icon_url icon_url, m.published published, m.approved approved, m.updated updated,\n m.team_id team_id, m.license license, m.slug slug, m.status status_name, m.color color,\n u.username username,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is false) categories,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories,\n ARRAY_AGG(DISTINCT lo.loader) filter (where lo.loader is not null) loaders,\n ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,\n ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games,\n ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is false) gallery,\n ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is true) featured_gallery,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'field_id', vf.field_id,\n 'int_value', vf.int_value,\n 'enum_value', vf.enum_value,\n 'string_value', vf.string_value\n )\n ) filter (where vf.field_id is not null) version_fields,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'lf_id', lf.id,\n 'loader_name', lo.loader,\n 'field', lf.field,\n 'field_type', lf.field_type,\n 'enum_type', lf.enum_type,\n 'min_val', lf.min_val,\n 'max_val', lf.max_val,\n 'optional', lf.optional\n )\n ) filter (where lf.id is not null) loader_fields,\n JSONB_AGG(\n DISTINCT jsonb_build_object(\n 'id', lfev.id,\n 'enum_id', lfev.enum_id,\n 'value', lfev.value,\n 'ordering', lfev.ordering,\n 'created', lfev.created,\n 'metadata', lfev.metadata\n ) \n ) filter (where lfev.id is not null) loader_field_enum_values\n\n FROM versions v\n INNER JOIN mods m ON v.mod_id = m.id AND m.status = ANY($2)\n LEFT OUTER JOIN mods_categories mc ON joining_mod_id = m.id\n LEFT OUTER JOIN categories c ON mc.joining_category_id = c.id\n LEFT OUTER JOIN loaders_versions lv ON lv.version_id = v.id\n LEFT OUTER JOIN loaders lo ON lo.id = lv.loader_id\n LEFT JOIN loaders_project_types lpt ON lpt.joining_loader_id = lo.id\n LEFT JOIN project_types pt ON pt.id = lpt.joining_project_type_id\n LEFT JOIN loaders_project_types_games lptg ON lptg.loader_id = lo.id AND lptg.project_type_id = pt.id\n LEFT JOIN games g ON lptg.game_id = g.id\n LEFT OUTER JOIN mods_gallery mg ON mg.mod_id = m.id\n INNER JOIN team_members tm ON tm.team_id = m.team_id AND tm.role = $3 AND tm.accepted = TRUE\n INNER JOIN users u ON tm.user_id = u.id\n LEFT OUTER JOIN version_fields vf on v.id = vf.version_id\n LEFT OUTER JOIN loader_fields lf on vf.field_id = lf.id\n LEFT OUTER JOIN loader_field_enums lfe on lf.enum_type = lfe.id\n LEFT OUTER JOIN loader_field_enum_values lfev on lfev.enum_id = lfe.id\n WHERE v.status != ANY($1)\n GROUP BY v.id, m.id, u.id;\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "version_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "downloads",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "follows",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "published",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "approved",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "updated",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "team_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "license",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "slug",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "status_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "color",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "categories",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "additional_categories",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "loaders",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"name": "project_types",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"name": "games",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"name": "gallery",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"name": "featured_gallery",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"name": "version_fields",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"name": "loader_fields",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"name": "loader_field_enum_values",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray",
|
||||
"TextArray",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "d622e6108a96a13d254a489047c7760e1acfa9c41e157f6d75d2538abdba5e4e"
|
||||
}
|
||||
1
migrations/20241110010322_adds_game_version_minmax.sql
Normal file
1
migrations/20241110010322_adds_game_version_minmax.sql
Normal file
@ -0,0 +1 @@
|
||||
UPDATE loader_fields SET min_val = 1 WHERE field = 'game_versions';
|
||||
@ -15,7 +15,7 @@ use crate::models::ids::OAuthClientId;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use actix_web::http::header::LOCATION;
|
||||
use actix_web::web::{scope, Data, Query, ServiceConfig};
|
||||
use actix_web::web::{Data, Query, ServiceConfig};
|
||||
use actix_web::{get, post, web, HttpRequest, HttpResponse};
|
||||
use chrono::Duration;
|
||||
use rand::distributions::Alphanumeric;
|
||||
@ -33,13 +33,10 @@ pub mod errors;
|
||||
pub mod uris;
|
||||
|
||||
pub fn config(cfg: &mut ServiceConfig) {
|
||||
cfg.service(
|
||||
scope("auth/oauth")
|
||||
.service(init_oauth)
|
||||
.service(accept_client_scopes)
|
||||
.service(reject_client_scopes)
|
||||
.service(request_token),
|
||||
);
|
||||
cfg.service(init_oauth)
|
||||
.service(accept_client_scopes)
|
||||
.service(reject_client_scopes)
|
||||
.service(request_token);
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
||||
@ -295,60 +295,97 @@ pub struct SideType {
|
||||
impl LoaderField {
|
||||
pub async fn get_field<'a, E>(
|
||||
field: &str,
|
||||
loader_ids: &[LoaderId],
|
||||
exec: E,
|
||||
redis: &RedisPool,
|
||||
) -> Result<Option<LoaderField>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let fields = Self::get_fields(exec, redis).await?;
|
||||
let fields = Self::get_fields(loader_ids, exec, redis).await?;
|
||||
Ok(fields.into_iter().find(|f| f.field == field))
|
||||
}
|
||||
|
||||
// Gets all fields for a given loader
|
||||
// Gets all fields for a given loader(s)
|
||||
// Returns all as this there are probably relatively few fields per loader
|
||||
// TODO: in the future, this should be to get all fields in relation to something
|
||||
// - e.g. get all fields for a given game?
|
||||
pub async fn get_fields<'a, E>(
|
||||
loader_ids: &[LoaderId],
|
||||
exec: E,
|
||||
redis: &RedisPool,
|
||||
) -> Result<Vec<LoaderField>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let cached_fields = redis
|
||||
.get_deserialized_from_json(LOADER_FIELDS_NAMESPACE, 0) // 0 => whatever we search for fields by
|
||||
.await?;
|
||||
if let Some(cached_fields) = cached_fields {
|
||||
return Ok(cached_fields);
|
||||
type RedisLoaderFieldTuple = (LoaderId, Vec<LoaderField>);
|
||||
|
||||
let mut loader_ids = loader_ids.to_vec();
|
||||
let cached_fields: Vec<RedisLoaderFieldTuple> = redis
|
||||
.multi_get::<String, _>(LOADER_FIELDS_NAMESPACE, loader_ids.iter().map(|x| x.0))
|
||||
.await?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|x: String| serde_json::from_str::<RedisLoaderFieldTuple>(&x).ok())
|
||||
.collect();
|
||||
|
||||
let mut found_loader_fields = vec![];
|
||||
if !cached_fields.is_empty() {
|
||||
for (loader_id, fields) in cached_fields {
|
||||
if loader_ids.contains(&loader_id) {
|
||||
found_loader_fields.extend(fields);
|
||||
loader_ids.retain(|x| x != &loader_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT lf.id, lf.field, lf.field_type, lf.optional, lf.min_val, lf.max_val, lf.enum_type
|
||||
FROM loader_fields lf
|
||||
",
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().and_then(|r| {
|
||||
Some(LoaderField {
|
||||
id: LoaderFieldId(r.id),
|
||||
field_type: LoaderFieldType::build(&r.field_type, r.enum_type)?,
|
||||
field: r.field,
|
||||
optional: r.optional,
|
||||
min_val: r.min_val,
|
||||
max_val: r.max_val,
|
||||
})
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<LoaderField>>()
|
||||
.await?;
|
||||
|
||||
redis
|
||||
.set_serialized_to_json(LOADER_FIELDS_NAMESPACE, &0, &result, None)
|
||||
if !loader_ids.is_empty() {
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT DISTINCT lf.id, lf.field, lf.field_type, lf.optional, lf.min_val, lf.max_val, lf.enum_type, lfl.loader_id
|
||||
FROM loader_fields lf
|
||||
LEFT JOIN loader_fields_loaders lfl ON lfl.loader_field_id = lf.id
|
||||
WHERE lfl.loader_id = ANY($1)
|
||||
",
|
||||
&loader_ids.iter().map(|x| x.0).collect::<Vec<_>>()
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().and_then(|r| {
|
||||
Some((LoaderId(r.loader_id) ,LoaderField {
|
||||
id: LoaderFieldId(r.id),
|
||||
field_type: LoaderFieldType::build(&r.field_type, r.enum_type)?,
|
||||
field: r.field,
|
||||
optional: r.optional,
|
||||
min_val: r.min_val,
|
||||
max_val: r.max_val,
|
||||
}))
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<(LoaderId, LoaderField)>>()
|
||||
.await?;
|
||||
|
||||
let result: Vec<RedisLoaderFieldTuple> = result
|
||||
.into_iter()
|
||||
.fold(
|
||||
HashMap::new(),
|
||||
|mut acc: HashMap<LoaderId, Vec<LoaderField>>, x| {
|
||||
acc.entry(x.0).or_default().push(x.1);
|
||||
acc
|
||||
},
|
||||
)
|
||||
.into_iter()
|
||||
.collect_vec();
|
||||
|
||||
for (k, v) in result.into_iter() {
|
||||
redis
|
||||
.set_serialized_to_json(LOADER_FIELDS_NAMESPACE, k.0, (k, &v), None)
|
||||
.await?;
|
||||
found_loader_fields.extend(v);
|
||||
}
|
||||
}
|
||||
let result = found_loader_fields
|
||||
.into_iter()
|
||||
.unique_by(|x| x.id)
|
||||
.collect();
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
@ -641,6 +678,41 @@ impl VersionField {
|
||||
enum_variants: Vec<LoaderFieldEnumValue>,
|
||||
) -> Result<VersionField, String> {
|
||||
let value = VersionFieldValue::parse(&loader_field, value, enum_variants)?;
|
||||
|
||||
// Ensure, if applicable, that the value is within the min/max bounds
|
||||
let countable = match &value {
|
||||
VersionFieldValue::Integer(i) => Some(*i),
|
||||
VersionFieldValue::ArrayInteger(v) => Some(v.len() as i32),
|
||||
VersionFieldValue::Text(_) => None,
|
||||
VersionFieldValue::ArrayText(v) => Some(v.len() as i32),
|
||||
VersionFieldValue::Boolean(_) => None,
|
||||
VersionFieldValue::ArrayBoolean(v) => Some(v.len() as i32),
|
||||
VersionFieldValue::Enum(_, _) => None,
|
||||
VersionFieldValue::ArrayEnum(_, v) => Some(v.len() as i32),
|
||||
};
|
||||
|
||||
if let Some(count) = countable {
|
||||
if let Some(min) = loader_field.min_val {
|
||||
if count < min {
|
||||
return Err(format!(
|
||||
"Provided value '{v}' for {field_name} is less than the minimum of {min}",
|
||||
v = serde_json::to_string(&value).unwrap_or_default(),
|
||||
field_name = loader_field.field,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(max) = loader_field.max_val {
|
||||
if count > max {
|
||||
return Err(format!(
|
||||
"Provided value '{v}' for {field_name} is greater than the maximum of {max}",
|
||||
v = serde_json::to_string(&value).unwrap_or_default(),
|
||||
field_name = loader_field.field,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(VersionField {
|
||||
version_id,
|
||||
field_id: loader_field.id,
|
||||
|
||||
@ -42,7 +42,7 @@ bitflags_serde_impl!(ProjectPermissions, u64);
|
||||
|
||||
impl Default for ProjectPermissions {
|
||||
fn default() -> ProjectPermissions {
|
||||
ProjectPermissions::UPLOAD_VERSION | ProjectPermissions::DELETE_VERSION
|
||||
ProjectPermissions::empty()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@ pub fn config(cfg: &mut actix_web::web::ServiceConfig) {
|
||||
.wrap(default_cors())
|
||||
.configure(admin::config)
|
||||
.configure(analytics_get::config)
|
||||
// Todo: separate these- they need to also follow v2-v3 conversion
|
||||
.configure(crate::auth::session::config)
|
||||
.configure(crate::auth::flows::config)
|
||||
.configure(crate::auth::pats::config)
|
||||
|
||||
@ -67,13 +67,15 @@ pub async fn project_search(
|
||||
facet
|
||||
.into_iter()
|
||||
.map(|facet| {
|
||||
let version = match facet.split(':').nth(1) {
|
||||
Some(version) => version,
|
||||
let val = match facet.split(':').nth(1) {
|
||||
Some(val) => val,
|
||||
None => return facet.to_string(),
|
||||
};
|
||||
|
||||
if facet.starts_with("versions:") {
|
||||
format!("game_versions:{}", version)
|
||||
format!("game_versions:{}", val)
|
||||
} else if facet.starts_with("project_type:") {
|
||||
format!("project_types:{}", val)
|
||||
} else {
|
||||
facet.to_string()
|
||||
}
|
||||
|
||||
@ -95,6 +95,11 @@ pub async fn version_create(
|
||||
json!(legacy_create.game_versions),
|
||||
);
|
||||
|
||||
// TODO: will be overhauled with side-types overhaul
|
||||
// TODO: if not, should default to previous version
|
||||
fields.insert("client_side".to_string(), json!("required"));
|
||||
fields.insert("server_side".to_string(), json!("optional"));
|
||||
|
||||
// TODO: Some kind of handling here to ensure project type is fine.
|
||||
// We expect the version uploaded to be of loader type modpack, but there might not be a way to check here for that.
|
||||
// After all, theoretically, they could be creating a genuine 'fabric' mod, and modpack no longer carries information on whether its a mod or modpack,
|
||||
|
||||
322
src/routes/v3/admin.rs
Normal file
322
src/routes/v3/admin.rs
Normal file
@ -0,0 +1,322 @@
|
||||
use crate::auth::validate::get_user_record_from_bearer_token;
|
||||
use crate::database::models::User;
|
||||
use crate::database::redis::RedisPool;
|
||||
use crate::models::analytics::Download;
|
||||
use crate::models::ids::ProjectId;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::users::{PayoutStatus, RecipientStatus};
|
||||
use crate::queue::analytics::AnalyticsQueue;
|
||||
use crate::queue::maxmind::MaxMindIndexer;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::routes::ApiError;
|
||||
use crate::search::SearchConfig;
|
||||
use crate::util::date::get_current_tenths_of_ms;
|
||||
use crate::util::guards::admin_key_guard;
|
||||
use crate::util::routes::read_from_payload;
|
||||
use actix_web::{patch, post, web, HttpRequest, HttpResponse};
|
||||
use hex::ToHex;
|
||||
use hmac::{Hmac, Mac, NewMac};
|
||||
use serde::Deserialize;
|
||||
use sha2::Sha256;
|
||||
use sqlx::PgPool;
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("admin")
|
||||
.service(count_download)
|
||||
.service(trolley_webhook)
|
||||
.service(force_reindex),
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DownloadBody {
|
||||
pub url: String,
|
||||
pub project_id: ProjectId,
|
||||
pub version_name: String,
|
||||
|
||||
pub ip: String,
|
||||
pub headers: HashMap<String, String>,
|
||||
}
|
||||
|
||||
// This is an internal route, cannot be used without key
|
||||
#[patch("/_count-download", guard = "admin_key_guard")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn count_download(
|
||||
req: HttpRequest,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<RedisPool>,
|
||||
maxmind: web::Data<Arc<MaxMindIndexer>>,
|
||||
analytics_queue: web::Data<Arc<AnalyticsQueue>>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
download_body: web::Json<DownloadBody>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let token = download_body
|
||||
.headers
|
||||
.iter()
|
||||
.find(|x| x.0.to_lowercase() == "authorization")
|
||||
.map(|x| &**x.1);
|
||||
|
||||
let user = get_user_record_from_bearer_token(&req, token, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
let project_id: crate::database::models::ids::ProjectId = download_body.project_id.into();
|
||||
|
||||
let id_option = crate::models::ids::base62_impl::parse_base62(&download_body.version_name)
|
||||
.ok()
|
||||
.map(|x| x as i64);
|
||||
|
||||
let (version_id, project_id) = if let Some(version) = sqlx::query!(
|
||||
"
|
||||
SELECT v.id id, v.mod_id mod_id FROM files f
|
||||
INNER JOIN versions v ON v.id = f.version_id
|
||||
WHERE f.url = $1
|
||||
",
|
||||
download_body.url,
|
||||
)
|
||||
.fetch_optional(pool.as_ref())
|
||||
.await?
|
||||
{
|
||||
(version.id, version.mod_id)
|
||||
} else if let Some(version) = sqlx::query!(
|
||||
"
|
||||
SELECT id, mod_id FROM versions
|
||||
WHERE ((version_number = $1 OR id = $3) AND mod_id = $2)
|
||||
",
|
||||
download_body.version_name,
|
||||
project_id as crate::database::models::ids::ProjectId,
|
||||
id_option
|
||||
)
|
||||
.fetch_optional(pool.as_ref())
|
||||
.await?
|
||||
{
|
||||
(version.id, version.mod_id)
|
||||
} else {
|
||||
return Err(ApiError::InvalidInput(
|
||||
"Specified version does not exist!".to_string(),
|
||||
));
|
||||
};
|
||||
|
||||
let url = url::Url::parse(&download_body.url)
|
||||
.map_err(|_| ApiError::InvalidInput("invalid download URL specified!".to_string()))?;
|
||||
|
||||
let ip = crate::routes::analytics::convert_to_ip_v6(&download_body.ip)
|
||||
.unwrap_or_else(|_| Ipv4Addr::new(127, 0, 0, 1).to_ipv6_mapped());
|
||||
|
||||
analytics_queue.add_download(Download {
|
||||
id: Uuid::new_v4(),
|
||||
recorded: get_current_tenths_of_ms(),
|
||||
domain: url.host_str().unwrap_or_default().to_string(),
|
||||
site_path: url.path().to_string(),
|
||||
user_id: user
|
||||
.and_then(|(scopes, x)| {
|
||||
if scopes.contains(Scopes::PERFORM_ANALYTICS) {
|
||||
Some(x.id.0 as u64)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or(0),
|
||||
project_id: project_id as u64,
|
||||
version_id: version_id as u64,
|
||||
ip,
|
||||
country: maxmind.query(ip).await.unwrap_or_default(),
|
||||
user_agent: download_body
|
||||
.headers
|
||||
.get("user-agent")
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
headers: download_body
|
||||
.headers
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|x| !crate::routes::analytics::FILTERED_HEADERS.contains(&&*x.0.to_lowercase()))
|
||||
.collect(),
|
||||
});
|
||||
|
||||
Ok(HttpResponse::NoContent().body(""))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct TrolleyWebhook {
|
||||
model: String,
|
||||
action: String,
|
||||
body: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
#[post("/_trolley")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn trolley_webhook(
|
||||
req: HttpRequest,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<RedisPool>,
|
||||
mut payload: web::Payload,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
if let Some(signature) = req.headers().get("X-PaymentRails-Signature") {
|
||||
let payload = read_from_payload(
|
||||
&mut payload,
|
||||
1 << 20,
|
||||
"Webhook payload exceeds the maximum of 1MiB.",
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut signature = signature.to_str().ok().unwrap_or_default().split(',');
|
||||
let timestamp = signature
|
||||
.next()
|
||||
.and_then(|x| x.split('=').nth(1))
|
||||
.unwrap_or_default();
|
||||
let v1 = signature
|
||||
.next()
|
||||
.and_then(|x| x.split('=').nth(1))
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut mac: Hmac<Sha256> =
|
||||
Hmac::new_from_slice(dotenvy::var("TROLLEY_WEBHOOK_SIGNATURE")?.as_bytes())
|
||||
.map_err(|_| ApiError::Payments("error initializing HMAC".to_string()))?;
|
||||
mac.update(timestamp.as_bytes());
|
||||
mac.update(&payload);
|
||||
let request_signature = mac.finalize().into_bytes().encode_hex::<String>();
|
||||
|
||||
if &*request_signature == v1 {
|
||||
let webhook = serde_json::from_slice::<TrolleyWebhook>(&payload)?;
|
||||
|
||||
if webhook.model == "recipient" {
|
||||
#[derive(Deserialize)]
|
||||
struct Recipient {
|
||||
pub id: String,
|
||||
pub email: Option<String>,
|
||||
pub status: Option<RecipientStatus>,
|
||||
}
|
||||
|
||||
if let Some(body) = webhook.body.get("recipient") {
|
||||
if let Ok(recipient) = serde_json::from_value::<Recipient>(body.clone()) {
|
||||
let value = sqlx::query!(
|
||||
"SELECT id FROM users WHERE trolley_id = $1",
|
||||
recipient.id
|
||||
)
|
||||
.fetch_optional(&**pool)
|
||||
.await?;
|
||||
|
||||
if let Some(user) = value {
|
||||
let user = User::get_id(
|
||||
crate::database::models::UserId(user.id),
|
||||
&**pool,
|
||||
&redis,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(user) = user {
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
if webhook.action == "deleted" {
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users
|
||||
SET trolley_account_status = NULL, trolley_id = NULL
|
||||
WHERE id = $1
|
||||
",
|
||||
user.id.0
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users
|
||||
SET email = $1, email_verified = $2, trolley_account_status = $3
|
||||
WHERE id = $4
|
||||
",
|
||||
recipient.email.clone(),
|
||||
user.email_verified && recipient.email == user.email,
|
||||
recipient.status.map(|x| x.as_str()),
|
||||
user.id.0
|
||||
)
|
||||
.execute(&mut *transaction).await?;
|
||||
}
|
||||
|
||||
transaction.commit().await?;
|
||||
User::clear_caches(&[(user.id, None)], &redis).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if webhook.model == "payment" {
|
||||
#[derive(Deserialize)]
|
||||
struct Payment {
|
||||
pub id: String,
|
||||
pub status: PayoutStatus,
|
||||
}
|
||||
|
||||
if let Some(body) = webhook.body.get("payment") {
|
||||
if let Ok(payment) = serde_json::from_value::<Payment>(body.clone()) {
|
||||
let value = sqlx::query!(
|
||||
"SELECT id, amount, user_id, status FROM historical_payouts WHERE payment_id = $1",
|
||||
payment.id
|
||||
)
|
||||
.fetch_optional(&**pool)
|
||||
.await?;
|
||||
|
||||
if let Some(payout) = value {
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
if payment.status.is_failed()
|
||||
&& !PayoutStatus::from_string(&payout.status).is_failed()
|
||||
{
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE users
|
||||
SET balance = balance + $1
|
||||
WHERE id = $2
|
||||
",
|
||||
payout.amount,
|
||||
payout.user_id,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE historical_payouts
|
||||
SET status = $1
|
||||
WHERE payment_id = $2
|
||||
",
|
||||
payment.status.as_str(),
|
||||
payment.id,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
User::clear_caches(
|
||||
&[(crate::database::models::UserId(payout.user_id), None)],
|
||||
&redis,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(HttpResponse::NoContent().finish())
|
||||
}
|
||||
|
||||
#[post("/_force_reindex", guard = "admin_key_guard")]
|
||||
pub async fn force_reindex(
|
||||
pool: web::Data<PgPool>,
|
||||
config: web::Data<SearchConfig>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
use crate::search::indexing::index_projects;
|
||||
index_projects(pool.as_ref().clone(), &config).await?;
|
||||
Ok(HttpResponse::NoContent().finish())
|
||||
}
|
||||
@ -1,8 +1,9 @@
|
||||
pub use super::ApiError;
|
||||
use crate::{auth::oauth, util::cors::default_cors};
|
||||
use crate::util::cors::default_cors;
|
||||
use actix_web::{web, HttpResponse};
|
||||
use serde_json::json;
|
||||
|
||||
pub mod admin;
|
||||
pub mod analytics_get;
|
||||
pub mod collections;
|
||||
pub mod images;
|
||||
@ -27,20 +28,28 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("v3")
|
||||
.wrap(default_cors())
|
||||
.configure(admin::config)
|
||||
.configure(analytics_get::config)
|
||||
// TODO: write tests that catch these
|
||||
.configure(oauth_clients::config)
|
||||
.configure(crate::auth::session::config)
|
||||
.configure(crate::auth::flows::config)
|
||||
.configure(crate::auth::pats::config)
|
||||
.configure(collections::config)
|
||||
.configure(images::config)
|
||||
.configure(moderation::config)
|
||||
.configure(notifications::config)
|
||||
.configure(organizations::config)
|
||||
.configure(project_creation::config)
|
||||
.configure(projects::config)
|
||||
.configure(reports::config)
|
||||
.configure(statistics::config)
|
||||
.configure(tags::config)
|
||||
.configure(teams::config)
|
||||
.configure(threads::config)
|
||||
.configure(users::config)
|
||||
.configure(version_file::config)
|
||||
.configure(versions::config)
|
||||
.configure(oauth::config)
|
||||
.configure(oauth_clients::config),
|
||||
.configure(versions::config),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -43,20 +43,19 @@ use crate::database::models::oauth_client_item::OAuthClient as DBOAuthClient;
|
||||
use crate::models::ids::OAuthClientId as ApiOAuthClientId;
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(get_user_clients);
|
||||
cfg.service(
|
||||
scope("oauth")
|
||||
.configure(crate::auth::oauth::config)
|
||||
.service(revoke_oauth_authorization)
|
||||
.service(oauth_client_create)
|
||||
.service(oauth_client_edit)
|
||||
.service(oauth_client_delete)
|
||||
.service(get_client)
|
||||
.service(get_clients)
|
||||
.service(get_user_oauth_authorizations)
|
||||
.service(revoke_oauth_authorization),
|
||||
.service(get_user_oauth_authorizations),
|
||||
);
|
||||
}
|
||||
|
||||
#[get("user/{user_id}/oauth_apps")]
|
||||
pub async fn get_user_clients(
|
||||
req: HttpRequest,
|
||||
info: web::Path<String>,
|
||||
@ -354,6 +353,7 @@ pub async fn revoke_oauth_authorization(
|
||||
redis: web::Data<RedisPool>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
println!("Inside revoke_oauth_authorization");
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
|
||||
@ -23,15 +23,17 @@ use sqlx::PgPool;
|
||||
use validator::Validate;
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.route("organizations", web::get().to(organizations_get));
|
||||
cfg.service(
|
||||
web::scope("organization")
|
||||
.route("", web::post().to(organization_create))
|
||||
.route("{id}/projects", web::get().to(organization_projects_get))
|
||||
.route("{id}", web::get().to(organization_get))
|
||||
.route("{id}", web::patch().to(organizations_edit))
|
||||
.route("{id}", web::delete().to(organization_delete))
|
||||
.route("{id}/projects", web::post().to(organization_projects_add))
|
||||
.route(
|
||||
"{id}/projects",
|
||||
"{id}/projects/{project_id}",
|
||||
web::delete().to(organization_projects_remove),
|
||||
)
|
||||
.route("{id}/icon", web::patch().to(organization_icon_edit))
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
use super::version_creation::InitialVersionData;
|
||||
use super::version_creation::{try_create_version_fields, InitialVersionData};
|
||||
use crate::auth::{get_user_from_headers, AuthenticationError};
|
||||
use crate::database::models::loader_fields::{
|
||||
Loader, LoaderField, LoaderFieldEnumValue, VersionField,
|
||||
};
|
||||
use crate::database::models::loader_fields::{Loader, LoaderField, LoaderFieldEnumValue};
|
||||
use crate::database::models::thread_item::ThreadBuilder;
|
||||
use crate::database::models::{self, image_item, User};
|
||||
use crate::database::redis::RedisPool;
|
||||
@ -37,7 +35,7 @@ use thiserror::Error;
|
||||
use validator::Validate;
|
||||
|
||||
pub fn config(cfg: &mut actix_web::web::ServiceConfig) {
|
||||
cfg.route("create", web::post().to(project_create));
|
||||
cfg.route("project", web::post().to(project_create));
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@ -884,31 +882,19 @@ async fn create_initial_version(
|
||||
})
|
||||
.collect::<Result<Vec<models::LoaderId>, CreateError>>()?;
|
||||
|
||||
let loader_fields = LoaderField::get_fields(&mut **transaction, redis).await?;
|
||||
let mut version_fields = vec![];
|
||||
let loader_fields = LoaderField::get_fields(&loaders, &mut **transaction, redis).await?;
|
||||
let mut loader_field_enum_values =
|
||||
LoaderFieldEnumValue::list_many_loader_fields(&loader_fields, &mut **transaction, redis)
|
||||
.await?;
|
||||
for (key, value) in version_data.fields.iter() {
|
||||
let loader_field = loader_fields
|
||||
.iter()
|
||||
.find(|lf| &lf.field == key)
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(format!("Loader field '{key}' does not exist!"))
|
||||
})?;
|
||||
let enum_variants = loader_field_enum_values
|
||||
.remove(&loader_field.id)
|
||||
.unwrap_or_default();
|
||||
let vf: VersionField = VersionField::check_parse(
|
||||
version_id.into(),
|
||||
loader_field.clone(),
|
||||
value.clone(),
|
||||
enum_variants,
|
||||
)
|
||||
.map_err(CreateError::InvalidInput)?;
|
||||
version_fields.push(vf);
|
||||
}
|
||||
|
||||
let version_fields = try_create_version_fields(
|
||||
version_id,
|
||||
&version_data.fields,
|
||||
&loader_fields,
|
||||
&mut loader_field_enum_values,
|
||||
)?;
|
||||
|
||||
println!("Made it past here");
|
||||
let dependencies = version_data
|
||||
.dependencies
|
||||
.iter()
|
||||
|
||||
@ -43,7 +43,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
web::scope("project")
|
||||
.route("{id}", web::get().to(project_get))
|
||||
.route("{id}/check", web::get().to(project_get_check))
|
||||
.route("{id}", web::delete().to(project_get))
|
||||
.route("{id}", web::delete().to(project_delete))
|
||||
.route("{id}", web::patch().to(project_edit))
|
||||
.route("{id}/icon", web::patch().to(project_icon_edit))
|
||||
.route("{id}/icon", web::delete().to(delete_project_icon))
|
||||
@ -59,7 +59,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
"members",
|
||||
web::get().to(super::teams::team_members_get_project),
|
||||
)
|
||||
.route("versions", web::get().to(super::versions::version_list))
|
||||
.route("version", web::get().to(super::versions::version_list))
|
||||
.route(
|
||||
"version/{slug}",
|
||||
web::get().to(super::versions::version_project_get),
|
||||
|
||||
@ -7,6 +7,7 @@ use crate::database::models::loader_fields::{
|
||||
};
|
||||
use crate::database::redis::RedisPool;
|
||||
use actix_web::{web, HttpResponse};
|
||||
use itertools::Itertools;
|
||||
use serde_json::Value;
|
||||
use sqlx::PgPool;
|
||||
|
||||
@ -17,7 +18,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
.route("loader", web::get().to(loader_list)),
|
||||
)
|
||||
.route("games", web::get().to(games_list))
|
||||
.route("loader_fields", web::get().to(loader_fields_list))
|
||||
.route("loader_field", web::get().to(loader_fields_list))
|
||||
.route("license", web::get().to(license_list))
|
||||
.route("license/{id}", web::get().to(license_text))
|
||||
.route("donation_platform", web::get().to(donation_platform_list))
|
||||
@ -118,14 +119,20 @@ pub async fn loader_fields_list(
|
||||
redis: web::Data<RedisPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let query = query.into_inner();
|
||||
let loader_field = LoaderField::get_field(&query.loader_field, &**pool, &redis)
|
||||
let all_loader_ids = Loader::list(&**pool, &redis)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInput(format!(
|
||||
"'{}' was not a valid loader field.",
|
||||
query.loader_field
|
||||
))
|
||||
})?;
|
||||
.into_iter()
|
||||
.map(|x| x.id)
|
||||
.collect_vec();
|
||||
let loader_field =
|
||||
LoaderField::get_field(&query.loader_field, &all_loader_ids, &**pool, &redis)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInput(format!(
|
||||
"'{}' was not a valid loader field.",
|
||||
query.loader_field
|
||||
))
|
||||
})?;
|
||||
|
||||
let loader_field_enum_id = match loader_field.field_type {
|
||||
LoaderFieldType::Enum(enum_id) | LoaderFieldType::ArrayEnum(enum_id) => enum_id,
|
||||
|
||||
@ -24,8 +24,8 @@ use sqlx::PgPool;
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("thread")
|
||||
.route("{id}", web::get().to(thread_get))
|
||||
.route("inbox", web::get().to(moderation_inbox))
|
||||
.route("{id}", web::get().to(thread_get))
|
||||
.route("{id}", web::post().to(thread_send_message))
|
||||
.route("{id}/read", web::post().to(thread_read)),
|
||||
);
|
||||
@ -517,7 +517,6 @@ pub async fn moderation_inbox(
|
||||
Some(&[Scopes::THREAD_READ]),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let ids = sqlx::query!(
|
||||
"
|
||||
SELECT id
|
||||
|
||||
@ -26,7 +26,7 @@ use crate::{
|
||||
util::{routes::read_from_payload, validate::validation_errors_to_string},
|
||||
};
|
||||
|
||||
use super::ApiError;
|
||||
use super::{oauth_clients::get_user_clients, ApiError};
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.route("user", web::get().to(user_auth_get));
|
||||
@ -45,7 +45,8 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
.route("{id}/notifications", web::get().to(user_notifications))
|
||||
.route("{id}/payouts", web::get().to(user_payouts))
|
||||
.route("{id}/payouts_fees", web::get().to(user_payouts_fees))
|
||||
.route("{id}/payouts", web::post().to(user_payouts_request)),
|
||||
.route("{id}/payouts", web::post().to(user_payouts_request))
|
||||
.route("{id}/oauth_apps", web::get().to(get_user_clients)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ use futures::stream::StreamExt;
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::postgres::PgPool;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use validator::Validate;
|
||||
|
||||
@ -256,37 +256,6 @@ async fn version_create_inner(
|
||||
|
||||
let all_loaders =
|
||||
models::loader_fields::Loader::list(&mut **transaction, redis).await?;
|
||||
|
||||
let loader_fields = LoaderField::get_fields(&mut **transaction, redis).await?;
|
||||
let mut version_fields = vec![];
|
||||
let mut loader_field_enum_values = LoaderFieldEnumValue::list_many_loader_fields(
|
||||
&loader_fields,
|
||||
&mut **transaction,
|
||||
redis,
|
||||
)
|
||||
.await?;
|
||||
for (key, value) in version_create_data.fields.iter() {
|
||||
let loader_field = loader_fields
|
||||
.iter()
|
||||
.find(|lf| &lf.field == key)
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(format!(
|
||||
"Loader field '{key}' does not exist!"
|
||||
))
|
||||
})?;
|
||||
let enum_variants = loader_field_enum_values
|
||||
.remove(&loader_field.id)
|
||||
.unwrap_or_default();
|
||||
let vf: VersionField = VersionField::check_parse(
|
||||
version_id.into(),
|
||||
loader_field.clone(),
|
||||
value.clone(),
|
||||
enum_variants,
|
||||
)
|
||||
.map_err(CreateError::InvalidInput)?;
|
||||
version_fields.push(vf);
|
||||
}
|
||||
|
||||
let loaders = version_create_data
|
||||
.loaders
|
||||
.iter()
|
||||
@ -299,7 +268,22 @@ async fn version_create_inner(
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
selected_loaders = Some(loaders.clone());
|
||||
let loader_ids = loaders.iter().map(|y| y.id).collect_vec();
|
||||
let loader_ids: Vec<models::LoaderId> = loaders.iter().map(|y| y.id).collect_vec();
|
||||
|
||||
let loader_fields =
|
||||
LoaderField::get_fields(&loader_ids, &mut **transaction, redis).await?;
|
||||
let mut loader_field_enum_values = LoaderFieldEnumValue::list_many_loader_fields(
|
||||
&loader_fields,
|
||||
&mut **transaction,
|
||||
redis,
|
||||
)
|
||||
.await?;
|
||||
let version_fields = try_create_version_fields(
|
||||
version_id,
|
||||
&version_create_data.fields,
|
||||
&loader_fields,
|
||||
&mut loader_field_enum_values,
|
||||
)?;
|
||||
|
||||
let dependencies = version_create_data
|
||||
.dependencies
|
||||
@ -966,3 +950,50 @@ pub fn get_name_ext(
|
||||
};
|
||||
Ok((file_name, file_extension))
|
||||
}
|
||||
|
||||
// Reused functionality between project_creation and version_creation
|
||||
// Create a list of VersionFields from the fetched data, and check that all mandatory fields are present
|
||||
pub fn try_create_version_fields(
|
||||
version_id: VersionId,
|
||||
submitted_fields: &HashMap<String, serde_json::Value>,
|
||||
loader_fields: &[LoaderField],
|
||||
loader_field_enum_values: &mut HashMap<models::LoaderFieldId, Vec<LoaderFieldEnumValue>>,
|
||||
) -> Result<Vec<VersionField>, CreateError> {
|
||||
let mut version_fields = vec![];
|
||||
let mut remaining_mandatory_loader_fields = loader_fields
|
||||
.iter()
|
||||
.filter(|lf| !lf.optional)
|
||||
.map(|lf| lf.field.clone())
|
||||
.collect::<HashSet<_>>();
|
||||
for (key, value) in submitted_fields.iter() {
|
||||
let loader_field = loader_fields
|
||||
.iter()
|
||||
.find(|lf| &lf.field == key)
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(format!(
|
||||
"Loader field '{key}' does not exist for any loaders supplied,"
|
||||
))
|
||||
})?;
|
||||
remaining_mandatory_loader_fields.remove(&loader_field.field);
|
||||
let enum_variants = loader_field_enum_values
|
||||
.remove(&loader_field.id)
|
||||
.unwrap_or_default();
|
||||
|
||||
let vf: VersionField = VersionField::check_parse(
|
||||
version_id.into(),
|
||||
loader_field.clone(),
|
||||
value.clone(),
|
||||
enum_variants,
|
||||
)
|
||||
.map_err(CreateError::InvalidInput)?;
|
||||
version_fields.push(vf);
|
||||
}
|
||||
|
||||
if !remaining_mandatory_loader_fields.is_empty() {
|
||||
return Err(CreateError::InvalidInput(format!(
|
||||
"Missing mandatory loader fields: {}",
|
||||
remaining_mandatory_loader_fields.iter().join(", ")
|
||||
)));
|
||||
}
|
||||
Ok(version_fields)
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ use std::collections::HashMap;
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("version_file")
|
||||
.route("version_id", web::get().to(get_version_from_hash))
|
||||
.route("{version_id}", web::get().to(get_version_from_hash))
|
||||
.route("{version_id}/update", web::post().to(get_update_from_hash))
|
||||
.route("project", web::post().to(get_projects_from_hashes))
|
||||
.route("{version_id}", web::delete().to(delete_file))
|
||||
@ -380,7 +380,7 @@ pub async fn update_files(
|
||||
Ok(HttpResponse::Ok().json(response))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct FileUpdateData {
|
||||
pub hash: String,
|
||||
pub loaders: Option<Vec<String>>,
|
||||
@ -388,7 +388,7 @@ pub struct FileUpdateData {
|
||||
pub version_types: Option<Vec<VersionType>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ManyFileUpdateData {
|
||||
#[serde(default = "default_algorithm")]
|
||||
pub algorithm: String,
|
||||
@ -461,6 +461,7 @@ pub async fn update_individual_files(
|
||||
if let Some(loaders) = &query_file.loaders {
|
||||
bool &= x.loaders.iter().any(|y| loaders.contains(y));
|
||||
}
|
||||
|
||||
if let Some(loader_fields) = &query_file.loader_fields {
|
||||
for (key, values) in loader_fields {
|
||||
bool &= if let Some(x_vf) =
|
||||
@ -472,7 +473,6 @@ pub async fn update_individual_files(
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
})
|
||||
.sorted()
|
||||
|
||||
@ -5,7 +5,9 @@ use crate::auth::{
|
||||
filter_authorized_versions, get_user_from_headers, is_authorized, is_authorized_version,
|
||||
};
|
||||
use crate::database;
|
||||
use crate::database::models::loader_fields::{LoaderField, LoaderFieldEnumValue, VersionField};
|
||||
use crate::database::models::loader_fields::{
|
||||
self, LoaderField, LoaderFieldEnumValue, VersionField,
|
||||
};
|
||||
use crate::database::models::version_item::{DependencyBuilder, LoaderVersion};
|
||||
use crate::database::models::{image_item, Organization};
|
||||
use crate::database::redis::RedisPool;
|
||||
@ -22,6 +24,7 @@ use crate::util::img;
|
||||
use crate::util::validate::validation_errors_to_string;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use chrono::{DateTime, Utc};
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
use validator::Validate;
|
||||
@ -384,7 +387,14 @@ pub async fn version_edit_helper(
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let loader_fields = LoaderField::get_fields(&mut *transaction, &redis)
|
||||
let all_loaders = loader_fields::Loader::list(&mut *transaction, &redis).await?;
|
||||
let loader_ids = version_item
|
||||
.loaders
|
||||
.iter()
|
||||
.filter_map(|x| all_loaders.iter().find(|y| &y.loader == x).map(|y| y.id))
|
||||
.collect_vec();
|
||||
|
||||
let loader_fields = LoaderField::get_fields(&loader_ids, &mut *transaction, &redis)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|lf| version_fields_names.contains(&lf.field))
|
||||
@ -417,7 +427,7 @@ pub async fn version_edit_helper(
|
||||
.find(|lf| lf.field == vf_name)
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInput(format!(
|
||||
"Loader field '{vf_name}' does not exist."
|
||||
"Loader field '{vf_name}' does not exist for any loaders supplied."
|
||||
))
|
||||
})?;
|
||||
let enum_variants = loader_field_enum_values
|
||||
|
||||
@ -22,7 +22,7 @@ pub async fn index_local(
|
||||
SELECT m.id id, v.id version_id, m.title title, m.description description, m.downloads downloads, m.follows follows,
|
||||
m.icon_url icon_url, m.published published, m.approved approved, m.updated updated,
|
||||
m.team_id team_id, m.license license, m.slug slug, m.status status_name, m.color color,
|
||||
pt.name project_type_name, u.username username,
|
||||
u.username username,
|
||||
ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is false) categories,
|
||||
ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories,
|
||||
ARRAY_AGG(DISTINCT lo.loader) filter (where lo.loader is not null) loaders,
|
||||
@ -79,7 +79,7 @@ pub async fn index_local(
|
||||
LEFT OUTER JOIN loader_field_enums lfe on lf.enum_type = lfe.id
|
||||
LEFT OUTER JOIN loader_field_enum_values lfev on lfev.enum_id = lfe.id
|
||||
WHERE v.status != ANY($1)
|
||||
GROUP BY v.id, m.id, pt.id, u.id;
|
||||
GROUP BY v.id, m.id, u.id;
|
||||
",
|
||||
&*crate::models::projects::VersionStatus::iterator().filter(|x| x.is_hidden()).map(|x| x.to_string()).collect::<Vec<String>>(),
|
||||
&*crate::models::projects::ProjectStatus::iterator().filter(|x| x.is_searchable()).map(|x| x.to_string()).collect::<Vec<String>>(),
|
||||
@ -146,7 +146,7 @@ pub async fn index_local(
|
||||
modified_timestamp: m.updated.timestamp(),
|
||||
license,
|
||||
slug: m.slug,
|
||||
project_type: m.project_type_name,
|
||||
project_types: m.project_types.unwrap_or_default(),
|
||||
gallery: m.gallery.unwrap_or_default(),
|
||||
display_categories,
|
||||
open_source,
|
||||
|
||||
@ -176,7 +176,7 @@ fn default_settings() -> Settings {
|
||||
const DEFAULT_DISPLAYED_ATTRIBUTES: &[&str] = &[
|
||||
"project_id",
|
||||
"version_id",
|
||||
"project_type",
|
||||
"project_types",
|
||||
"slug",
|
||||
"author",
|
||||
"title",
|
||||
@ -200,7 +200,7 @@ const DEFAULT_SEARCHABLE_ATTRIBUTES: &[&str] = &["title", "description", "author
|
||||
const DEFAULT_ATTRIBUTES_FOR_FACETING: &[&str] = &[
|
||||
"categories",
|
||||
"license",
|
||||
"project_type",
|
||||
"project_types",
|
||||
"downloads",
|
||||
"follows",
|
||||
"author",
|
||||
|
||||
@ -74,7 +74,7 @@ impl SearchConfig {
|
||||
pub struct UploadSearchProject {
|
||||
pub version_id: String,
|
||||
pub project_id: String,
|
||||
pub project_type: String,
|
||||
pub project_types: Vec<String>,
|
||||
pub slug: Option<String>,
|
||||
pub author: String,
|
||||
pub title: String,
|
||||
@ -114,7 +114,7 @@ pub struct SearchResults {
|
||||
pub struct ResultSearchProject {
|
||||
pub version_id: String,
|
||||
pub project_id: String,
|
||||
pub project_type: String,
|
||||
pub project_types: Vec<String>,
|
||||
pub slug: Option<String>,
|
||||
pub author: String,
|
||||
pub title: String,
|
||||
|
||||
@ -87,7 +87,7 @@ pub async fn send_discord_webhook(
|
||||
"
|
||||
SELECT m.id id, m.title title, m.description description, m.color color,
|
||||
m.icon_url icon_url, m.slug slug,
|
||||
pt.name project_type, u.username username, u.avatar_url avatar_url,
|
||||
u.username username, u.avatar_url avatar_url,
|
||||
ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null) categories,
|
||||
ARRAY_AGG(DISTINCT lo.loader) filter (where lo.loader is not null) loaders,
|
||||
ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,
|
||||
@ -142,7 +142,7 @@ pub async fn send_discord_webhook(
|
||||
LEFT OUTER JOIN loader_field_enums lfe on lf.enum_type = lfe.id
|
||||
LEFT OUTER JOIN loader_field_enum_values lfev on lfev.enum_id = lfe.id
|
||||
WHERE m.id = $1
|
||||
GROUP BY m.id, pt.id, u.id;
|
||||
GROUP BY m.id, u.id;
|
||||
",
|
||||
project_id.0 as i64,
|
||||
&*crate::models::projects::VersionStatus::iterator().filter(|x| x.is_hidden()).map(|x| x.to_string()).collect::<Vec<String>>(),
|
||||
@ -246,7 +246,8 @@ pub async fn send_discord_webhook(
|
||||
});
|
||||
}
|
||||
|
||||
let mut project_type = project.project_type;
|
||||
let mut project_types: Vec<String> = project.project_types.unwrap_or_default();
|
||||
let mut project_type = project_types.pop().unwrap_or_default(); // TODO: Should this grab a not-first?
|
||||
|
||||
if loaders.iter().all(|x| PLUGIN_LOADERS.contains(&&**x)) {
|
||||
project_type = "plugin".to_string();
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use common::database::*;
|
||||
use common::environment::TestEnvironment;
|
||||
use itertools::Itertools;
|
||||
use labrinth::models::ids::base62_impl::parse_base62;
|
||||
use rust_decimal::{prelude::ToPrimitive, Decimal};
|
||||
|
||||
use crate::common::environment::TestEnvironment;
|
||||
|
||||
// importing common module.
|
||||
mod common;
|
||||
|
||||
#[actix_rt::test]
|
||||
pub async fn analytics_revenue() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let alpha_project_id = test_env
|
||||
.dummy
|
||||
|
||||
@ -4,8 +4,8 @@ use super::environment::LocalService;
|
||||
use actix_web::dev::ServiceResponse;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub mod organization;
|
||||
pub mod project;
|
||||
pub mod request_data;
|
||||
pub mod tags;
|
||||
pub mod team;
|
||||
pub mod version;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::common::api_v2::request_data::ProjectCreationRequestData;
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::{
|
||||
dev::ServiceResponse,
|
||||
@ -14,14 +13,11 @@ use labrinth::{
|
||||
};
|
||||
use rust_decimal::Decimal;
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::common::{
|
||||
asserts::assert_status,
|
||||
database::MOD_USER_PAT,
|
||||
request_data::{ImageData, ProjectCreationRequestData},
|
||||
};
|
||||
use crate::common::{asserts::assert_status, database::MOD_USER_PAT};
|
||||
|
||||
use super::ApiV2;
|
||||
use super::{request_data::ImageData, ApiV2};
|
||||
|
||||
impl ApiV2 {
|
||||
pub async fn add_public_project(
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#![allow(dead_code)]
|
||||
use serde_json::json;
|
||||
|
||||
use super::dummy_data::{DummyImage, TestFile};
|
||||
use crate::common::dummy_data::{DummyImage, TestFile};
|
||||
use labrinth::{
|
||||
models::projects::ProjectId,
|
||||
util::actix::{MultipartSegment, MultipartSegmentData},
|
||||
@ -12,9 +12,9 @@ use labrinth::{
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::{asserts::assert_status, request_data::VersionCreationRequestData};
|
||||
use crate::common::asserts::assert_status;
|
||||
|
||||
use super::ApiV2;
|
||||
use super::{request_data::VersionCreationRequestData, ApiV2};
|
||||
|
||||
pub fn url_encode_json_serialized_vec(elements: &[String]) -> String {
|
||||
let serialized = serde_json::to_string(&elements).unwrap();
|
||||
@ -327,8 +327,7 @@ impl ApiV2 {
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
// TODO: remove redundancy in these functions
|
||||
|
||||
// TODO: remove redundancy in these functions- some are essentially repeats
|
||||
pub async fn create_default_version(
|
||||
&self,
|
||||
project_id: &str,
|
||||
|
||||
@ -6,7 +6,12 @@ use std::rc::Rc;
|
||||
|
||||
pub mod oauth;
|
||||
pub mod oauth_clients;
|
||||
pub mod organization;
|
||||
pub mod project;
|
||||
pub mod request_data;
|
||||
pub mod tags;
|
||||
pub mod team;
|
||||
pub mod version;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ApiV3 {
|
||||
@ -17,4 +22,15 @@ impl ApiV3 {
|
||||
pub async fn call(&self, req: actix_http::Request) -> ServiceResponse {
|
||||
self.test_app.call(req).await.unwrap()
|
||||
}
|
||||
|
||||
pub async fn reset_search_index(&self) -> ServiceResponse {
|
||||
let req = actix_web::test::TestRequest::post()
|
||||
.uri("/v3/admin/_force_reindex")
|
||||
.append_header((
|
||||
"Modrinth-Admin",
|
||||
dotenvy::var("LABRINTH_ADMIN_KEY").unwrap(),
|
||||
))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ impl ApiV3 {
|
||||
pub async fn oauth_accept(&self, flow: &str, pat: &str) -> ServiceResponse {
|
||||
self.call(
|
||||
TestRequest::post()
|
||||
.uri("/v3/auth/oauth/accept")
|
||||
.uri("/v3/oauth/accept")
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.set_json(RespondToOAuthClientScopes {
|
||||
flow: flow.to_string(),
|
||||
@ -68,7 +68,7 @@ impl ApiV3 {
|
||||
pub async fn oauth_reject(&self, flow: &str, pat: &str) -> ServiceResponse {
|
||||
self.call(
|
||||
TestRequest::post()
|
||||
.uri("/v3/auth/oauth/reject")
|
||||
.uri("/v3/oauth/reject")
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.set_json(RespondToOAuthClientScopes {
|
||||
flow: flow.to_string(),
|
||||
@ -87,7 +87,7 @@ impl ApiV3 {
|
||||
) -> ServiceResponse {
|
||||
self.call(
|
||||
TestRequest::post()
|
||||
.uri("/v3/auth/oauth/token")
|
||||
.uri("/v3/oauth/token")
|
||||
.append_header((AUTHORIZATION, client_secret))
|
||||
.set_form(TokenRequest {
|
||||
grant_type: "authorization_code".to_string(),
|
||||
@ -108,7 +108,7 @@ pub fn generate_authorize_uri(
|
||||
state: Option<&str>,
|
||||
) -> String {
|
||||
format!(
|
||||
"/v3/auth/oauth/authorize?client_id={}{}{}{}",
|
||||
"/v3/oauth/authorize?client_id={}{}{}{}",
|
||||
urlencoding::encode(client_id),
|
||||
optional_query_param("redirect_uri", redirect_uri),
|
||||
optional_query_param("scope", scope),
|
||||
|
||||
@ -3,14 +3,12 @@ use actix_web::{
|
||||
test::{self, TestRequest},
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use labrinth::models::{organizations::Organization, v2::projects::LegacyProject};
|
||||
use labrinth::models::{organizations::Organization, v3::projects::Project};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::request_data::ImageData;
|
||||
use super::{request_data::ImageData, ApiV3};
|
||||
|
||||
use super::ApiV2;
|
||||
|
||||
impl ApiV2 {
|
||||
impl ApiV3 {
|
||||
pub async fn create_organization(
|
||||
&self,
|
||||
organization_title: &str,
|
||||
@ -18,7 +16,7 @@ impl ApiV2 {
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/organization")
|
||||
.uri("/v3/organization")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(json!({
|
||||
"title": organization_title,
|
||||
@ -30,7 +28,7 @@ impl ApiV2 {
|
||||
|
||||
pub async fn get_organization(&self, id_or_title: &str, pat: &str) -> ServiceResponse {
|
||||
let req = TestRequest::get()
|
||||
.uri(&format!("/v2/organization/{id_or_title}"))
|
||||
.uri(&format!("/v3/organization/{id_or_title}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
@ -48,7 +46,7 @@ impl ApiV2 {
|
||||
|
||||
pub async fn get_organization_projects(&self, id_or_title: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/organization/{id_or_title}/projects"))
|
||||
.uri(&format!("/v3/organization/{id_or_title}/projects"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
@ -58,7 +56,7 @@ impl ApiV2 {
|
||||
&self,
|
||||
id_or_title: &str,
|
||||
pat: &str,
|
||||
) -> Vec<LegacyProject> {
|
||||
) -> Vec<Project> {
|
||||
let resp = self.get_organization_projects(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
@ -71,7 +69,7 @@ impl ApiV2 {
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/organization/{id_or_title}"))
|
||||
.uri(&format!("/v3/organization/{id_or_title}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(patch)
|
||||
.to_request();
|
||||
@ -89,7 +87,7 @@ impl ApiV2 {
|
||||
// If an icon is provided, upload it
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/organization/{id_or_title}/icon?ext={ext}",
|
||||
"/v3/organization/{id_or_title}/icon?ext={ext}",
|
||||
ext = icon.extension
|
||||
))
|
||||
.append_header(("Authorization", pat))
|
||||
@ -100,7 +98,7 @@ impl ApiV2 {
|
||||
} else {
|
||||
// If no icon is provided, delete the icon
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!("/v2/organization/{id_or_title}/icon"))
|
||||
.uri(&format!("/v3/organization/{id_or_title}/icon"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
|
||||
@ -110,7 +108,7 @@ impl ApiV2 {
|
||||
|
||||
pub async fn delete_organization(&self, id_or_title: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!("/v2/organization/{id_or_title}"))
|
||||
.uri(&format!("/v3/organization/{id_or_title}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
|
||||
@ -124,7 +122,7 @@ impl ApiV2 {
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/organization/{id_or_title}/projects"))
|
||||
.uri(&format!("/v3/organization/{id_or_title}/projects"))
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(json!({
|
||||
"project_id": project_id_or_slug,
|
||||
@ -142,7 +140,7 @@ impl ApiV2 {
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!(
|
||||
"/v2/organization/{id_or_title}/projects/{project_id_or_slug}"
|
||||
"/v3/organization/{id_or_title}/projects/{project_id_or_slug}"
|
||||
))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
256
tests/common/api_v3/project.rs
Normal file
256
tests/common/api_v3/project.rs
Normal file
@ -0,0 +1,256 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::{
|
||||
dev::ServiceResponse,
|
||||
test::{self, TestRequest},
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use chrono::{DateTime, Utc};
|
||||
use labrinth::{
|
||||
models::v3::projects::{Project, Version},
|
||||
search::SearchResults,
|
||||
util::actix::AppendsMultipart,
|
||||
};
|
||||
use rust_decimal::Decimal;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::{asserts::assert_status, database::MOD_USER_PAT};
|
||||
|
||||
use super::{
|
||||
request_data::{ImageData, ProjectCreationRequestData},
|
||||
ApiV3,
|
||||
};
|
||||
|
||||
impl ApiV3 {
|
||||
pub async fn add_public_project(
|
||||
&self,
|
||||
creation_data: ProjectCreationRequestData,
|
||||
pat: &str,
|
||||
) -> (Project, Vec<Version>) {
|
||||
// Add a project.
|
||||
let req = TestRequest::post()
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_multipart(creation_data.segment_data)
|
||||
.to_request();
|
||||
let resp = self.call(req).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
|
||||
// Approve as a moderator.
|
||||
let req = TestRequest::patch()
|
||||
.uri(&format!("/v3/project/{}", creation_data.slug))
|
||||
.append_header(("Authorization", MOD_USER_PAT))
|
||||
.set_json(json!(
|
||||
{
|
||||
"status": "approved"
|
||||
}
|
||||
))
|
||||
.to_request();
|
||||
let resp = self.call(req).await;
|
||||
assert_status(&resp, StatusCode::NO_CONTENT);
|
||||
|
||||
let project = self
|
||||
.get_project_deserialized(&creation_data.slug, pat)
|
||||
.await;
|
||||
|
||||
// Get project's versions
|
||||
let req = TestRequest::get()
|
||||
.uri(&format!("/v3/project/{}/version", creation_data.slug))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
let resp = self.call(req).await;
|
||||
let versions: Vec<Version> = test::read_body_json(resp).await;
|
||||
|
||||
(project, versions)
|
||||
}
|
||||
|
||||
pub async fn remove_project(&self, project_slug_or_id: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!("/v3/project/{project_slug_or_id}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
let resp = self.call(req).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
resp
|
||||
}
|
||||
|
||||
pub async fn get_project(&self, id_or_slug: &str, pat: &str) -> ServiceResponse {
|
||||
let req = TestRequest::get()
|
||||
.uri(&format!("/v3/project/{id_or_slug}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
pub async fn get_project_deserialized(&self, id_or_slug: &str, pat: &str) -> Project {
|
||||
let resp = self.get_project(id_or_slug, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_user_projects(&self, user_id_or_username: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v3/user/{}/projects", user_id_or_username))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_user_projects_deserialized(
|
||||
&self,
|
||||
user_id_or_username: &str,
|
||||
pat: &str,
|
||||
) -> Vec<Project> {
|
||||
let resp = self.get_user_projects(user_id_or_username, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn edit_project(
|
||||
&self,
|
||||
id_or_slug: &str,
|
||||
patch: serde_json::Value,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/project/{id_or_slug}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(patch)
|
||||
.to_request();
|
||||
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn edit_project_bulk(
|
||||
&self,
|
||||
ids_or_slugs: impl IntoIterator<Item = &str>,
|
||||
patch: serde_json::Value,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let projects_str = ids_or_slugs
|
||||
.into_iter()
|
||||
.map(|s| format!("\"{}\"", s))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v3/projects?ids={encoded}",
|
||||
encoded = urlencoding::encode(&format!("[{projects_str}]"))
|
||||
))
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(patch)
|
||||
.to_request();
|
||||
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn edit_project_icon(
|
||||
&self,
|
||||
id_or_slug: &str,
|
||||
icon: Option<ImageData>,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
if let Some(icon) = icon {
|
||||
// If an icon is provided, upload it
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v3/project/{id_or_slug}/icon?ext={ext}",
|
||||
ext = icon.extension
|
||||
))
|
||||
.append_header(("Authorization", pat))
|
||||
.set_payload(Bytes::from(icon.icon))
|
||||
.to_request();
|
||||
|
||||
self.call(req).await
|
||||
} else {
|
||||
// If no icon is provided, delete the icon
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!("/v3/project/{id_or_slug}/icon"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
|
||||
self.call(req).await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn search_deserialized(
|
||||
&self,
|
||||
query: Option<&str>,
|
||||
facets: Option<serde_json::Value>,
|
||||
pat: &str,
|
||||
) -> SearchResults {
|
||||
let query_field = if let Some(query) = query {
|
||||
format!("&query={}", urlencoding::encode(query))
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let facets_field = if let Some(facets) = facets {
|
||||
format!("&facets={}", urlencoding::encode(&facets.to_string()))
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v3/search?{}{}", query_field, facets_field))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
let resp = self.call(req).await;
|
||||
let status = resp.status();
|
||||
assert_eq!(status, 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_analytics_revenue(
|
||||
&self,
|
||||
id_or_slugs: Vec<&str>,
|
||||
start_date: Option<DateTime<Utc>>,
|
||||
end_date: Option<DateTime<Utc>>,
|
||||
resolution_minutes: Option<u32>,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let projects_string = serde_json::to_string(&id_or_slugs).unwrap();
|
||||
let projects_string = urlencoding::encode(&projects_string);
|
||||
|
||||
let mut extra_args = String::new();
|
||||
if let Some(start_date) = start_date {
|
||||
let start_date = start_date.to_rfc3339();
|
||||
// let start_date = serde_json::to_string(&start_date).unwrap();
|
||||
let start_date = urlencoding::encode(&start_date);
|
||||
extra_args.push_str(&format!("&start_date={start_date}"));
|
||||
}
|
||||
if let Some(end_date) = end_date {
|
||||
let end_date = end_date.to_rfc3339();
|
||||
// let end_date = serde_json::to_string(&end_date).unwrap();
|
||||
let end_date = urlencoding::encode(&end_date);
|
||||
extra_args.push_str(&format!("&end_date={end_date}"));
|
||||
}
|
||||
if let Some(resolution_minutes) = resolution_minutes {
|
||||
extra_args.push_str(&format!("&resolution_minutes={}", resolution_minutes));
|
||||
}
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!(
|
||||
"/v3/analytics/revenue?{projects_string}{extra_args}",
|
||||
))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_analytics_revenue_deserialized(
|
||||
&self,
|
||||
id_or_slugs: Vec<&str>,
|
||||
start_date: Option<DateTime<Utc>>,
|
||||
end_date: Option<DateTime<Utc>>,
|
||||
resolution_minutes: Option<u32>,
|
||||
pat: &str,
|
||||
) -> HashMap<String, HashMap<i64, Decimal>> {
|
||||
let resp = self
|
||||
.get_analytics_revenue(id_or_slugs, start_date, end_date, resolution_minutes, pat)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
}
|
||||
146
tests/common/api_v3/request_data.rs
Normal file
146
tests/common/api_v3/request_data.rs
Normal file
@ -0,0 +1,146 @@
|
||||
#![allow(dead_code)]
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::dummy_data::{DummyImage, TestFile};
|
||||
use labrinth::{
|
||||
models::projects::ProjectId,
|
||||
util::actix::{MultipartSegment, MultipartSegmentData},
|
||||
};
|
||||
|
||||
pub struct ProjectCreationRequestData {
|
||||
pub slug: String,
|
||||
pub jar: Option<TestFile>,
|
||||
pub segment_data: Vec<MultipartSegment>,
|
||||
}
|
||||
|
||||
pub struct VersionCreationRequestData {
|
||||
pub version: String,
|
||||
pub jar: Option<TestFile>,
|
||||
pub segment_data: Vec<MultipartSegment>,
|
||||
}
|
||||
|
||||
pub struct ImageData {
|
||||
pub filename: String,
|
||||
pub extension: String,
|
||||
pub icon: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn get_public_project_creation_data(
|
||||
slug: &str,
|
||||
version_jar: Option<TestFile>,
|
||||
) -> ProjectCreationRequestData {
|
||||
let json_data = get_public_project_creation_data_json(slug, version_jar.as_ref());
|
||||
let multipart_data = get_public_creation_data_multipart(&json_data, version_jar.as_ref());
|
||||
ProjectCreationRequestData {
|
||||
slug: slug.to_string(),
|
||||
jar: version_jar,
|
||||
segment_data: multipart_data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_public_version_creation_data(
|
||||
project_id: ProjectId,
|
||||
version_number: &str,
|
||||
version_jar: TestFile,
|
||||
// closure that takes in a &mut serde_json::Value
|
||||
// and modifies it before it is serialized and sent
|
||||
modify_json: Option<impl FnOnce(&mut serde_json::Value)>,
|
||||
) -> VersionCreationRequestData {
|
||||
let mut json_data = get_public_version_creation_data_json(version_number, &version_jar);
|
||||
json_data["project_id"] = json!(project_id);
|
||||
if let Some(modify_json) = modify_json {
|
||||
modify_json(&mut json_data);
|
||||
}
|
||||
|
||||
let multipart_data = get_public_creation_data_multipart(&json_data, Some(&version_jar));
|
||||
VersionCreationRequestData {
|
||||
version: version_number.to_string(),
|
||||
jar: Some(version_jar),
|
||||
segment_data: multipart_data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_public_version_creation_data_json(
|
||||
version_number: &str,
|
||||
version_jar: &TestFile,
|
||||
) -> serde_json::Value {
|
||||
let is_modpack = version_jar.project_type() == "modpack";
|
||||
let mut j = json!({
|
||||
"file_parts": [version_jar.filename()],
|
||||
"version_number": version_number,
|
||||
"version_title": "start",
|
||||
"dependencies": [],
|
||||
"release_channel": "release",
|
||||
"loaders": [if is_modpack { "mrpack" } else { "fabric" }],
|
||||
"featured": true,
|
||||
|
||||
// Loader fields
|
||||
"game_versions": ["1.20.1"],
|
||||
"client_side": "required",
|
||||
"server_side": "optional"
|
||||
});
|
||||
if is_modpack {
|
||||
j["mrpack_loaders"] = json!(["fabric"]);
|
||||
}
|
||||
j
|
||||
}
|
||||
|
||||
pub fn get_public_project_creation_data_json(
|
||||
slug: &str,
|
||||
version_jar: Option<&TestFile>,
|
||||
) -> serde_json::Value {
|
||||
let initial_versions = if let Some(jar) = version_jar {
|
||||
json!([get_public_version_creation_data_json("1.2.3", jar)])
|
||||
} else {
|
||||
json!([])
|
||||
};
|
||||
|
||||
let is_draft = version_jar.is_none();
|
||||
json!(
|
||||
{
|
||||
"title": format!("Test Project {slug}"),
|
||||
"slug": slug,
|
||||
"description": "A dummy project for testing with.",
|
||||
"body": "This project is approved, and versions are listed.",
|
||||
"initial_versions": initial_versions,
|
||||
"is_draft": is_draft,
|
||||
"categories": [],
|
||||
"license_id": "MIT",
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_public_creation_data_multipart(
|
||||
json_data: &serde_json::Value,
|
||||
version_jar: Option<&TestFile>,
|
||||
) -> Vec<MultipartSegment> {
|
||||
// Basic json
|
||||
let json_segment = MultipartSegment {
|
||||
name: "data".to_string(),
|
||||
filename: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
data: MultipartSegmentData::Text(serde_json::to_string(json_data).unwrap()),
|
||||
};
|
||||
|
||||
if let Some(jar) = version_jar {
|
||||
// Basic file
|
||||
let file_segment = MultipartSegment {
|
||||
name: jar.filename(),
|
||||
filename: Some(jar.filename()),
|
||||
content_type: Some("application/java-archive".to_string()),
|
||||
data: MultipartSegmentData::Binary(jar.bytes()),
|
||||
};
|
||||
|
||||
vec![json_segment, file_segment]
|
||||
} else {
|
||||
vec![json_segment]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_icon_data(dummy_icon: DummyImage) -> ImageData {
|
||||
ImageData {
|
||||
filename: dummy_icon.filename(),
|
||||
extension: dummy_icon.extension(),
|
||||
icon: dummy_icon.bytes(),
|
||||
}
|
||||
}
|
||||
@ -3,12 +3,61 @@ use actix_web::{
|
||||
test::{self, TestRequest},
|
||||
};
|
||||
use labrinth::routes::v3::tags::GameData;
|
||||
use labrinth::{
|
||||
database::models::loader_fields::LoaderFieldEnumValue,
|
||||
routes::v3::tags::{CategoryData, LoaderData},
|
||||
};
|
||||
|
||||
use crate::common::database::ADMIN_USER_PAT;
|
||||
|
||||
use super::ApiV3;
|
||||
|
||||
impl ApiV3 {
|
||||
pub async fn get_loaders(&self) -> ServiceResponse {
|
||||
let req = TestRequest::get()
|
||||
.uri("/v3/tag/loader")
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_loaders_deserialized(&self) -> Vec<LoaderData> {
|
||||
let resp = self.get_loaders().await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_categories(&self) -> ServiceResponse {
|
||||
let req = TestRequest::get()
|
||||
.uri("/v3/tag/category")
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_categories_deserialized(&self) -> Vec<CategoryData> {
|
||||
let resp = self.get_categories().await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_loader_field_variants(&self, loader_field: &str) -> ServiceResponse {
|
||||
let req = TestRequest::get()
|
||||
.uri(&format!("/v3/loader_field?loader_field={}", loader_field))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_loader_field_variants_deserialized(
|
||||
&self,
|
||||
loader_field: &str,
|
||||
) -> Vec<LoaderFieldEnumValue> {
|
||||
let resp = self.get_loader_field_variants(loader_field).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
// TODO: fold this into v3 API of other v3 testing PR
|
||||
pub async fn get_games(&self) -> ServiceResponse {
|
||||
let req = TestRequest::get()
|
||||
|
||||
176
tests/common/api_v3/team.rs
Normal file
176
tests/common/api_v3/team.rs
Normal file
@ -0,0 +1,176 @@
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::{dev::ServiceResponse, test};
|
||||
use labrinth::models::{
|
||||
notifications::Notification,
|
||||
teams::{OrganizationPermissions, ProjectPermissions, TeamMember},
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::asserts::assert_status;
|
||||
|
||||
use super::ApiV3;
|
||||
|
||||
impl ApiV3 {
|
||||
pub async fn get_team_members(&self, id_or_title: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v3/team/{id_or_title}/members"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_team_members_deserialized(
|
||||
&self,
|
||||
id_or_title: &str,
|
||||
pat: &str,
|
||||
) -> Vec<TeamMember> {
|
||||
let resp = self.get_team_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_project_members(&self, id_or_title: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v3/project/{id_or_title}/members"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_project_members_deserialized(
|
||||
&self,
|
||||
id_or_title: &str,
|
||||
pat: &str,
|
||||
) -> Vec<TeamMember> {
|
||||
let resp = self.get_project_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_organization_members(&self, id_or_title: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v3/organization/{id_or_title}/members"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_organization_members_deserialized(
|
||||
&self,
|
||||
id_or_title: &str,
|
||||
pat: &str,
|
||||
) -> Vec<TeamMember> {
|
||||
let resp = self.get_organization_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn join_team(&self, team_id: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v3/team/{team_id}/join"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn remove_from_team(
|
||||
&self,
|
||||
team_id: &str,
|
||||
user_id: &str,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!("/v3/team/{team_id}/members/{user_id}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn edit_team_member(
|
||||
&self,
|
||||
team_id: &str,
|
||||
user_id: &str,
|
||||
patch: serde_json::Value,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{team_id}/members/{user_id}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(patch)
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn transfer_team_ownership(
|
||||
&self,
|
||||
team_id: &str,
|
||||
user_id: &str,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{team_id}/owner"))
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(json!({
|
||||
"user_id": user_id,
|
||||
}))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_user_notifications(&self, user_id: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v3/user/{user_id}/notifications"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_user_notifications_deserialized(
|
||||
&self,
|
||||
user_id: &str,
|
||||
pat: &str,
|
||||
) -> Vec<Notification> {
|
||||
let resp = self.get_user_notifications(user_id, pat).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn mark_notification_read(
|
||||
&self,
|
||||
notification_id: &str,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/notification/{notification_id}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
pub async fn add_user_to_team(
|
||||
&self,
|
||||
team_id: &str,
|
||||
user_id: &str,
|
||||
project_permissions: Option<ProjectPermissions>,
|
||||
organization_permissions: Option<OrganizationPermissions>,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v3/team/{team_id}/members"))
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(json!( {
|
||||
"user_id": user_id,
|
||||
"permissions" : project_permissions.map(|p| p.bits()).unwrap_or_default(),
|
||||
"organization_permissions" : organization_permissions.map(|p| p.bits()),
|
||||
}))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn delete_notification(&self, notification_id: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!("/v3/notification/{notification_id}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
}
|
||||
422
tests/common/api_v3/version.rs
Normal file
422
tests/common/api_v3/version.rs
Normal file
@ -0,0 +1,422 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use actix_http::{header::AUTHORIZATION, StatusCode};
|
||||
use actix_web::{
|
||||
dev::ServiceResponse,
|
||||
test::{self, TestRequest},
|
||||
};
|
||||
use labrinth::{
|
||||
models::{projects::VersionType, v3::projects::Version},
|
||||
routes::v3::version_file::FileUpdateData,
|
||||
util::actix::AppendsMultipart,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::asserts::assert_status;
|
||||
|
||||
use super::{request_data::VersionCreationRequestData, ApiV3};
|
||||
|
||||
pub fn url_encode_json_serialized_vec(elements: &[String]) -> String {
|
||||
let serialized = serde_json::to_string(&elements).unwrap();
|
||||
urlencoding::encode(&serialized).to_string()
|
||||
}
|
||||
|
||||
impl ApiV3 {
|
||||
pub async fn add_public_version(
|
||||
&self,
|
||||
creation_data: VersionCreationRequestData,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
// Add a project.
|
||||
let req = TestRequest::post()
|
||||
.uri("/v3/version")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_multipart(creation_data.segment_data)
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn add_public_version_deserialized(
|
||||
&self,
|
||||
creation_data: VersionCreationRequestData,
|
||||
pat: &str,
|
||||
) -> Version {
|
||||
let resp = self.add_public_version(creation_data, pat).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
let value: serde_json::Value = test::read_body_json(resp).await;
|
||||
let version_id = value["id"].as_str().unwrap();
|
||||
self.get_version_deserialized(version_id, pat).await
|
||||
}
|
||||
|
||||
pub async fn get_version(&self, id: &str, pat: &str) -> ServiceResponse {
|
||||
let req = TestRequest::get()
|
||||
.uri(&format!("/v3/version/{id}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_version_deserialized(&self, id: &str, pat: &str) -> Version {
|
||||
let resp = self.get_version(id, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn edit_version(
|
||||
&self,
|
||||
version_id: &str,
|
||||
patch: serde_json::Value,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/version/{version_id}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(patch)
|
||||
.to_request();
|
||||
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_version_from_hash(
|
||||
&self,
|
||||
hash: &str,
|
||||
algorithm: &str,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v3/version_file/{hash}?algorithm={algorithm}"))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_version_from_hash_deserialized(
|
||||
&self,
|
||||
hash: &str,
|
||||
algorithm: &str,
|
||||
pat: &str,
|
||||
) -> Version {
|
||||
let resp = self.get_version_from_hash(hash, algorithm, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_versions_from_hashes(
|
||||
&self,
|
||||
hashes: &[&str],
|
||||
algorithm: &str,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = TestRequest::post()
|
||||
.uri("/v3/version_files")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(json!({
|
||||
"hashes": hashes,
|
||||
"algorithm": algorithm,
|
||||
}))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_versions_from_hashes_deserialized(
|
||||
&self,
|
||||
hashes: &[&str],
|
||||
algorithm: &str,
|
||||
pat: &str,
|
||||
) -> HashMap<String, Version> {
|
||||
let resp = self.get_versions_from_hashes(hashes, algorithm, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_update_from_hash(
|
||||
&self,
|
||||
hash: &str,
|
||||
algorithm: &str,
|
||||
loaders: Option<Vec<String>>,
|
||||
game_versions: Option<Vec<String>>,
|
||||
version_types: Option<Vec<String>>,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let mut json = json!({});
|
||||
if let Some(loaders) = loaders {
|
||||
json["loaders"] = serde_json::to_value(loaders).unwrap();
|
||||
}
|
||||
if let Some(game_versions) = game_versions {
|
||||
json["loader_fields"] = json!({
|
||||
"game_versions": game_versions,
|
||||
});
|
||||
}
|
||||
if let Some(version_types) = version_types {
|
||||
json["version_types"] = serde_json::to_value(version_types).unwrap();
|
||||
}
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!(
|
||||
"/v3/version_file/{hash}/update?algorithm={algorithm}"
|
||||
))
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(json)
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn get_update_from_hash_deserialized(
|
||||
&self,
|
||||
hash: &str,
|
||||
algorithm: &str,
|
||||
loaders: Option<Vec<String>>,
|
||||
game_versions: Option<Vec<String>>,
|
||||
version_types: Option<Vec<String>>,
|
||||
pat: &str,
|
||||
) -> Version {
|
||||
let resp = self
|
||||
.get_update_from_hash(hash, algorithm, loaders, game_versions, version_types, pat)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn update_files(
|
||||
&self,
|
||||
algorithm: &str,
|
||||
hashes: Vec<String>,
|
||||
loaders: Option<Vec<String>>,
|
||||
game_versions: Option<Vec<String>>,
|
||||
version_types: Option<Vec<String>>,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let mut json = json!({
|
||||
"algorithm": algorithm,
|
||||
"hashes": hashes,
|
||||
});
|
||||
if let Some(loaders) = loaders {
|
||||
json["loaders"] = serde_json::to_value(loaders).unwrap();
|
||||
}
|
||||
if let Some(game_versions) = game_versions {
|
||||
json["loader_fields"] = json!({
|
||||
"game_versions": game_versions,
|
||||
});
|
||||
}
|
||||
if let Some(version_types) = version_types {
|
||||
json["version_types"] = serde_json::to_value(version_types).unwrap();
|
||||
}
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/version_files/update")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(json)
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn update_files_deserialized(
|
||||
&self,
|
||||
algorithm: &str,
|
||||
hashes: Vec<String>,
|
||||
loaders: Option<Vec<String>>,
|
||||
game_versions: Option<Vec<String>>,
|
||||
version_types: Option<Vec<String>>,
|
||||
pat: &str,
|
||||
) -> HashMap<String, Version> {
|
||||
let resp = self
|
||||
.update_files(
|
||||
algorithm,
|
||||
hashes,
|
||||
loaders,
|
||||
game_versions,
|
||||
version_types,
|
||||
pat,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn update_individual_files(
|
||||
&self,
|
||||
algorithm: &str,
|
||||
hashes: Vec<FileUpdateData>,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/version_files/update_individual")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(json!({
|
||||
"algorithm": algorithm,
|
||||
"hashes": hashes
|
||||
}))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
pub async fn update_individual_files_deserialized(
|
||||
&self,
|
||||
algorithm: &str,
|
||||
hashes: Vec<FileUpdateData>,
|
||||
pat: &str,
|
||||
) -> HashMap<String, Version> {
|
||||
let resp = self.update_individual_files(algorithm, hashes, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
// TODO: Not all fields are tested currently in the v3 tests, only the v2-v3 relevant ones are
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn get_project_versions(
|
||||
&self,
|
||||
project_id_slug: &str,
|
||||
game_versions: Option<Vec<String>>,
|
||||
loaders: Option<Vec<String>>,
|
||||
featured: Option<bool>,
|
||||
version_type: Option<VersionType>,
|
||||
limit: Option<usize>,
|
||||
offset: Option<usize>,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let mut query_string = String::new();
|
||||
if let Some(game_versions) = game_versions {
|
||||
query_string.push_str(&format!(
|
||||
"&game_versions={}",
|
||||
urlencoding::encode(&serde_json::to_string(&game_versions).unwrap())
|
||||
));
|
||||
}
|
||||
if let Some(loaders) = loaders {
|
||||
query_string.push_str(&format!(
|
||||
"&loaders={}",
|
||||
urlencoding::encode(&serde_json::to_string(&loaders).unwrap())
|
||||
));
|
||||
}
|
||||
if let Some(featured) = featured {
|
||||
query_string.push_str(&format!("&featured={}", featured));
|
||||
}
|
||||
if let Some(version_type) = version_type {
|
||||
query_string.push_str(&format!("&version_type={}", version_type));
|
||||
}
|
||||
if let Some(limit) = limit {
|
||||
let limit = limit.to_string();
|
||||
query_string.push_str(&format!("&limit={}", limit));
|
||||
}
|
||||
if let Some(offset) = offset {
|
||||
let offset = offset.to_string();
|
||||
query_string.push_str(&format!("&offset={}", offset));
|
||||
}
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!(
|
||||
"/v3/project/{project_id_slug}/version?{}",
|
||||
query_string.trim_start_matches('&')
|
||||
))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn get_project_versions_deserialized(
|
||||
&self,
|
||||
slug: &str,
|
||||
game_versions: Option<Vec<String>>,
|
||||
loaders: Option<Vec<String>>,
|
||||
featured: Option<bool>,
|
||||
version_type: Option<VersionType>,
|
||||
limit: Option<usize>,
|
||||
offset: Option<usize>,
|
||||
pat: &str,
|
||||
) -> Vec<Version> {
|
||||
let resp = self
|
||||
.get_project_versions(
|
||||
slug,
|
||||
game_versions,
|
||||
loaders,
|
||||
featured,
|
||||
version_type,
|
||||
limit,
|
||||
offset,
|
||||
pat,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
// TODO: remove redundancy in these functions
|
||||
|
||||
pub async fn create_default_version(
|
||||
&self,
|
||||
project_id: &str,
|
||||
ordering: Option<i32>,
|
||||
pat: &str,
|
||||
) -> Version {
|
||||
let json_data = json!(
|
||||
{
|
||||
"project_id": project_id,
|
||||
"file_parts": ["basic-mod-different.jar"],
|
||||
"version_number": "1.2.3.4",
|
||||
"version_title": "start",
|
||||
"dependencies": [],
|
||||
"game_versions": ["1.20.1"] ,
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"release_channel": "release",
|
||||
"loaders": ["fabric"],
|
||||
"featured": true,
|
||||
"ordering": ordering,
|
||||
}
|
||||
);
|
||||
let json_segment = labrinth::util::actix::MultipartSegment {
|
||||
name: "data".to_string(),
|
||||
filename: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
data: labrinth::util::actix::MultipartSegmentData::Text(
|
||||
serde_json::to_string(&json_data).unwrap(),
|
||||
),
|
||||
};
|
||||
let file_segment = labrinth::util::actix::MultipartSegment {
|
||||
name: "basic-mod-different.jar".to_string(),
|
||||
filename: Some("basic-mod.jar".to_string()),
|
||||
content_type: Some("application/java-archive".to_string()),
|
||||
data: labrinth::util::actix::MultipartSegmentData::Binary(
|
||||
include_bytes!("../../../tests/files/basic-mod-different.jar").to_vec(),
|
||||
),
|
||||
};
|
||||
|
||||
let request = test::TestRequest::post()
|
||||
.uri("/v3/version")
|
||||
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.to_request();
|
||||
let resp = self.call(request).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_versions(&self, version_ids: Vec<String>, pat: &str) -> Vec<Version> {
|
||||
let ids = url_encode_json_serialized_vec(&version_ids);
|
||||
let request = test::TestRequest::get()
|
||||
.uri(&format!("/v3/versions?ids={}", ids))
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.to_request();
|
||||
let resp = self.call(request).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn edit_version_ordering(
|
||||
&self,
|
||||
version_id: &str,
|
||||
ordering: Option<i32>,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let request = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/version/{version_id}"))
|
||||
.set_json(json!(
|
||||
{
|
||||
"ordering": ordering
|
||||
}
|
||||
))
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.to_request();
|
||||
self.call(request).await
|
||||
}
|
||||
}
|
||||
@ -2,13 +2,13 @@
|
||||
|
||||
use crate::common::get_json_val_str;
|
||||
use itertools::Itertools;
|
||||
use labrinth::models::v2::projects::LegacyVersion;
|
||||
use labrinth::models::v3::projects::Version;
|
||||
|
||||
pub fn assert_status(response: &actix_web::dev::ServiceResponse, status: actix_http::StatusCode) {
|
||||
assert_eq!(response.status(), status, "{:#?}", response.response());
|
||||
}
|
||||
|
||||
pub fn assert_version_ids(versions: &[LegacyVersion], expected_ids: Vec<String>) {
|
||||
pub fn assert_version_ids(versions: &[Version], expected_ids: Vec<String>) {
|
||||
let version_ids = versions
|
||||
.iter()
|
||||
.map(|v| get_json_val_str(v.id))
|
||||
|
||||
@ -7,7 +7,7 @@ use labrinth::models::{
|
||||
oauth_clients::OAuthClient,
|
||||
organizations::Organization,
|
||||
pats::Scopes,
|
||||
v2::projects::{LegacyProject, LegacyVersion},
|
||||
v3::projects::{Project, Version},
|
||||
};
|
||||
use serde_json::json;
|
||||
use sqlx::Executor;
|
||||
@ -16,7 +16,7 @@ use zip::{write::FileOptions, CompressionMethod, ZipWriter};
|
||||
use crate::common::database::USER_USER_PAT;
|
||||
use labrinth::util::actix::{AppendsMultipart, MultipartSegment, MultipartSegmentData};
|
||||
|
||||
use super::{environment::TestEnvironment, request_data::get_public_project_creation_data};
|
||||
use super::{api_v3::request_data::get_public_project_creation_data, environment::TestEnvironment};
|
||||
|
||||
use super::{asserts::assert_status, database::USER_USER_ID, get_json_val_str};
|
||||
|
||||
@ -170,10 +170,10 @@ pub struct DummyData {
|
||||
|
||||
impl DummyData {
|
||||
pub fn new(
|
||||
project_alpha: LegacyProject,
|
||||
project_alpha_version: LegacyVersion,
|
||||
project_beta: LegacyProject,
|
||||
project_beta_version: LegacyVersion,
|
||||
project_alpha: Project,
|
||||
project_alpha_version: Version,
|
||||
project_beta: Project,
|
||||
project_beta_version: Version,
|
||||
organization_zeta: Organization,
|
||||
oauth_client_alpha: OAuthClient,
|
||||
) -> Self {
|
||||
@ -303,9 +303,9 @@ pub async fn get_dummy_data(test_env: &TestEnvironment) -> DummyData {
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn add_project_alpha(test_env: &TestEnvironment) -> (LegacyProject, LegacyVersion) {
|
||||
pub async fn add_project_alpha(test_env: &TestEnvironment) -> (Project, Version) {
|
||||
let (project, versions) = test_env
|
||||
.v2
|
||||
.v3
|
||||
.add_public_project(
|
||||
get_public_project_creation_data("alpha", Some(TestFile::DummyProjectAlpha)),
|
||||
USER_USER_PAT,
|
||||
@ -314,25 +314,25 @@ pub async fn add_project_alpha(test_env: &TestEnvironment) -> (LegacyProject, Le
|
||||
(project, versions.into_iter().next().unwrap())
|
||||
}
|
||||
|
||||
pub async fn add_project_beta(test_env: &TestEnvironment) -> (LegacyProject, LegacyVersion) {
|
||||
pub async fn add_project_beta(test_env: &TestEnvironment) -> (Project, Version) {
|
||||
// Adds dummy data to the database with sqlx (projects, versions, threads)
|
||||
// Generate test project data.
|
||||
let jar = TestFile::DummyProjectBeta;
|
||||
// TODO: this shouldnt be hardcoded (nor should other similar ones be)
|
||||
let json_data = json!(
|
||||
{
|
||||
"title": "Test Project Beta",
|
||||
"slug": "beta",
|
||||
"description": "A dummy project for testing with.",
|
||||
"body": "This project is not-yet-approved, and versions are draft.",
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"initial_versions": [{
|
||||
"file_parts": [jar.filename()],
|
||||
"version_number": "1.2.3",
|
||||
"version_title": "start",
|
||||
"status": "unlisted",
|
||||
"requested_status": "unlisted",
|
||||
"dependencies": [],
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"game_versions": ["1.20.1"] ,
|
||||
"release_channel": "release",
|
||||
"loaders": ["fabric"],
|
||||
@ -363,12 +363,11 @@ pub async fn add_project_beta(test_env: &TestEnvironment) -> (LegacyProject, Leg
|
||||
|
||||
// Add a project.
|
||||
let req = TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
get_project_beta(test_env).await
|
||||
@ -377,7 +376,7 @@ pub async fn add_project_beta(test_env: &TestEnvironment) -> (LegacyProject, Leg
|
||||
pub async fn add_organization_zeta(test_env: &TestEnvironment) -> Organization {
|
||||
// Add an organzation.
|
||||
let req = TestRequest::post()
|
||||
.uri("/v2/organization")
|
||||
.uri("/v3/organization")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"title": "zeta",
|
||||
@ -391,46 +390,46 @@ pub async fn add_organization_zeta(test_env: &TestEnvironment) -> Organization {
|
||||
get_organization_zeta(test_env).await
|
||||
}
|
||||
|
||||
pub async fn get_project_alpha(test_env: &TestEnvironment) -> (LegacyProject, LegacyVersion) {
|
||||
pub async fn get_project_alpha(test_env: &TestEnvironment) -> (Project, Version) {
|
||||
// Get project
|
||||
let req = TestRequest::get()
|
||||
.uri("/v2/project/alpha")
|
||||
.uri("/v3/project/alpha")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let project: LegacyProject = test::read_body_json(resp).await;
|
||||
let project: Project = test::read_body_json(resp).await;
|
||||
|
||||
// Get project's versions
|
||||
let req = TestRequest::get()
|
||||
.uri("/v2/project/alpha/version")
|
||||
.uri("/v3/project/alpha/version")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let versions: Vec<LegacyVersion> = test::read_body_json(resp).await;
|
||||
let versions: Vec<Version> = test::read_body_json(resp).await;
|
||||
let version = versions.into_iter().next().unwrap();
|
||||
|
||||
(project, version)
|
||||
}
|
||||
|
||||
pub async fn get_project_beta(test_env: &TestEnvironment) -> (LegacyProject, LegacyVersion) {
|
||||
pub async fn get_project_beta(test_env: &TestEnvironment) -> (Project, Version) {
|
||||
// Get project
|
||||
let req = TestRequest::get()
|
||||
.uri("/v2/project/beta")
|
||||
.uri("/v3/project/beta")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
let project: serde_json::Value = test::read_body_json(resp).await;
|
||||
let project: LegacyProject = serde_json::from_value(project).unwrap();
|
||||
let project: Project = serde_json::from_value(project).unwrap();
|
||||
|
||||
// Get project's versions
|
||||
let req = TestRequest::get()
|
||||
.uri("/v2/project/beta/version")
|
||||
.uri("/v3/project/beta/version")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
let versions: Vec<LegacyVersion> = test::read_body_json(resp).await;
|
||||
let versions: Vec<Version> = test::read_body_json(resp).await;
|
||||
let version = versions.into_iter().next().unwrap();
|
||||
|
||||
(project, version)
|
||||
@ -439,7 +438,7 @@ pub async fn get_project_beta(test_env: &TestEnvironment) -> (LegacyProject, Leg
|
||||
pub async fn get_organization_zeta(test_env: &TestEnvironment) -> Organization {
|
||||
// Get organization
|
||||
let req = TestRequest::get()
|
||||
.uri("/v2/organization/zeta")
|
||||
.uri("/v3/organization/zeta")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
|
||||
@ -25,6 +25,9 @@ where
|
||||
|
||||
db.cleanup().await;
|
||||
}
|
||||
// TODO: This needs to be slightly redesigned in order to do both V2 and v3 tests.
|
||||
// TODO: Most tests, since they use API functions, can be applied to both. The ones that weren't are in v2/, but
|
||||
// all tests that can be applied to both should use both v2 and v3 (extract api to a trait with all the API functions and call both).
|
||||
|
||||
// A complete test environment, with a test actix app and a database.
|
||||
// Must be called in an #[actix_rt::test] context. It also simulates a
|
||||
@ -77,7 +80,7 @@ impl TestEnvironment {
|
||||
|
||||
pub async fn generate_friend_user_notification(&self) {
|
||||
let resp = self
|
||||
.v2
|
||||
.v3
|
||||
.add_user_to_team(
|
||||
&self.dummy.as_ref().unwrap().project_alpha.team_id,
|
||||
FRIEND_USER_ID,
|
||||
@ -95,7 +98,7 @@ impl TestEnvironment {
|
||||
pat: &str,
|
||||
status_code: StatusCode,
|
||||
) {
|
||||
let resp = self.v2.get_user_notifications(user_id, pat).await;
|
||||
let resp = self.v3.get_user_notifications(user_id, pat).await;
|
||||
assert_status(&resp, status_code);
|
||||
}
|
||||
|
||||
@ -105,7 +108,7 @@ impl TestEnvironment {
|
||||
pat: &str,
|
||||
status_code: StatusCode,
|
||||
) {
|
||||
let resp = self.v2.get_user_projects(user_id, pat).await;
|
||||
let resp = self.v3.get_user_projects(user_id, pat).await;
|
||||
assert_status(&resp, status_code);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ pub mod dummy_data;
|
||||
pub mod environment;
|
||||
pub mod pats;
|
||||
pub mod permissions;
|
||||
pub mod request_data;
|
||||
pub mod scopes;
|
||||
|
||||
// Testing equivalent to 'setup' function, producing a LabrinthConfig
|
||||
|
||||
@ -4,13 +4,11 @@ use itertools::Itertools;
|
||||
use labrinth::models::teams::{OrganizationPermissions, ProjectPermissions};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::{
|
||||
database::{generate_random_name, ADMIN_USER_PAT},
|
||||
request_data,
|
||||
};
|
||||
use crate::common::database::{generate_random_name, ADMIN_USER_PAT};
|
||||
|
||||
use super::{
|
||||
database::{USER_USER_ID, USER_USER_PAT},
|
||||
api_v3::request_data,
|
||||
database::{ENEMY_USER_PAT, USER_USER_ID, USER_USER_PAT},
|
||||
environment::TestEnvironment,
|
||||
};
|
||||
|
||||
@ -164,7 +162,48 @@ impl<'a> PermissionsTest<'a> {
|
||||
)
|
||||
.await;
|
||||
|
||||
// Failure test
|
||||
// Failure test- not logged in
|
||||
let request = req_gen(&PermissionsTestContext {
|
||||
project_id: Some(&project_id),
|
||||
team_id: Some(&team_id),
|
||||
..test_context
|
||||
})
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(request).await;
|
||||
if !self.allowed_failure_codes.contains(&resp.status().as_u16()) {
|
||||
return Err(format!(
|
||||
"Failure permissions test failed. Expected failure codes {} got {}",
|
||||
self.allowed_failure_codes
|
||||
.iter()
|
||||
.map(|code| code.to_string())
|
||||
.join(","),
|
||||
resp.status().as_u16()
|
||||
));
|
||||
}
|
||||
|
||||
// Failure test- logged in on a non-team user
|
||||
let request = req_gen(&PermissionsTestContext {
|
||||
project_id: Some(&project_id),
|
||||
team_id: Some(&team_id),
|
||||
..test_context
|
||||
})
|
||||
.append_header(("Authorization", ENEMY_USER_PAT))
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(request).await;
|
||||
if !self.allowed_failure_codes.contains(&resp.status().as_u16()) {
|
||||
return Err(format!(
|
||||
"Failure permissions test failed. Expected failure codes {} got {}",
|
||||
self.allowed_failure_codes
|
||||
.iter()
|
||||
.map(|code| code.to_string())
|
||||
.join(","),
|
||||
resp.status().as_u16()
|
||||
));
|
||||
}
|
||||
|
||||
// Failure test- logged in with EVERY non-relevant permission
|
||||
let request = req_gen(&PermissionsTestContext {
|
||||
project_id: Some(&project_id),
|
||||
team_id: Some(&team_id),
|
||||
@ -175,7 +214,6 @@ impl<'a> PermissionsTest<'a> {
|
||||
|
||||
let resp = test_env.call(request).await;
|
||||
if !self.allowed_failure_codes.contains(&resp.status().as_u16()) {
|
||||
println!("Body: {:?}", resp.response().body());
|
||||
return Err(format!(
|
||||
"Failure permissions test failed. Expected failure codes {} got {}",
|
||||
self.allowed_failure_codes
|
||||
@ -207,7 +245,6 @@ impl<'a> PermissionsTest<'a> {
|
||||
|
||||
let resp = test_env.call(request).await;
|
||||
if !resp.status().is_success() {
|
||||
println!("Body: {:?}", resp.response().body());
|
||||
return Err(format!(
|
||||
"Success permissions test failed. Expected success, got {}",
|
||||
resp.status().as_u16()
|
||||
@ -342,8 +379,9 @@ impl<'a> PermissionsTest<'a> {
|
||||
organization_team_id: None,
|
||||
};
|
||||
|
||||
// TEST 1: Failure
|
||||
// Random user, unaffiliated with the project, with no permissions
|
||||
// TEST 1: User not logged in - no PAT.
|
||||
// This should always fail, regardless of permissions
|
||||
// (As we are testing permissions-based failures)
|
||||
let test_1 = async {
|
||||
let (project_id, team_id) = create_dummy_project(test_env).await;
|
||||
|
||||
@ -379,18 +417,9 @@ impl<'a> PermissionsTest<'a> {
|
||||
};
|
||||
|
||||
// TEST 2: Failure
|
||||
// User affiliated with the project, with failure permissions
|
||||
// Random user, unaffiliated with the project, with no permissions
|
||||
let test_2 = async {
|
||||
let (project_id, team_id) = create_dummy_project(test_env).await;
|
||||
add_user_to_team(
|
||||
self.user_id,
|
||||
self.user_pat,
|
||||
&team_id,
|
||||
Some(failure_project_permissions),
|
||||
None,
|
||||
test_env,
|
||||
)
|
||||
.await;
|
||||
|
||||
let request = req_gen(&PermissionsTestContext {
|
||||
project_id: Some(&project_id),
|
||||
@ -399,7 +428,6 @@ impl<'a> PermissionsTest<'a> {
|
||||
})
|
||||
.append_header(("Authorization", self.user_pat))
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(request).await;
|
||||
if !self.allowed_failure_codes.contains(&resp.status().as_u16()) {
|
||||
return Err(format!(
|
||||
@ -412,11 +440,57 @@ impl<'a> PermissionsTest<'a> {
|
||||
));
|
||||
}
|
||||
|
||||
let p =
|
||||
get_project_permissions(self.user_id, self.user_pat, &project_id, test_env).await;
|
||||
if p != ProjectPermissions::empty() {
|
||||
return Err(format!(
|
||||
"Test 2 failed. Expected no permissions, got {:?}",
|
||||
p
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// TEST 3: Failure
|
||||
// User affiliated with the project, with failure permissions
|
||||
let test_3 = async {
|
||||
let (project_id, team_id) = create_dummy_project(test_env).await;
|
||||
add_user_to_team(
|
||||
self.user_id,
|
||||
self.user_pat,
|
||||
&team_id,
|
||||
Some(failure_project_permissions),
|
||||
None,
|
||||
test_env,
|
||||
)
|
||||
.await;
|
||||
|
||||
let request = req_gen(&PermissionsTestContext {
|
||||
project_id: Some(&project_id),
|
||||
team_id: Some(&team_id),
|
||||
..test_context
|
||||
})
|
||||
.append_header(("Authorization", self.user_pat))
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(request).await;
|
||||
if !self.allowed_failure_codes.contains(&resp.status().as_u16()) {
|
||||
return Err(format!(
|
||||
"Test 3 failed. Expected failure codes {} got {}",
|
||||
self.allowed_failure_codes
|
||||
.iter()
|
||||
.map(|code| code.to_string())
|
||||
.join(","),
|
||||
resp.status().as_u16()
|
||||
));
|
||||
}
|
||||
|
||||
let p =
|
||||
get_project_permissions(self.user_id, self.user_pat, &project_id, test_env).await;
|
||||
if p != failure_project_permissions {
|
||||
return Err(format!(
|
||||
"Test 2 failed. Expected {:?}, got {:?}",
|
||||
"Test 3 failed. Expected {:?}, got {:?}",
|
||||
failure_project_permissions, p
|
||||
));
|
||||
}
|
||||
@ -424,9 +498,9 @@ impl<'a> PermissionsTest<'a> {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// TEST 3: Success
|
||||
// TEST 4: Success
|
||||
// User affiliated with the project, with the given permissions
|
||||
let test_3 = async {
|
||||
let test_4 = async {
|
||||
let (project_id, team_id) = create_dummy_project(test_env).await;
|
||||
add_user_to_team(
|
||||
self.user_id,
|
||||
@ -449,7 +523,7 @@ impl<'a> PermissionsTest<'a> {
|
||||
let resp = test_env.call(request).await;
|
||||
if !resp.status().is_success() {
|
||||
return Err(format!(
|
||||
"Test 3 failed. Expected success, got {}",
|
||||
"Test 4 failed. Expected success, got {}",
|
||||
resp.status().as_u16()
|
||||
));
|
||||
}
|
||||
@ -458,7 +532,7 @@ impl<'a> PermissionsTest<'a> {
|
||||
get_project_permissions(self.user_id, self.user_pat, &project_id, test_env).await;
|
||||
if p != success_permissions {
|
||||
return Err(format!(
|
||||
"Test 3 failed. Expected {:?}, got {:?}",
|
||||
"Test 4 failed. Expected {:?}, got {:?}",
|
||||
success_permissions, p
|
||||
));
|
||||
}
|
||||
@ -466,10 +540,10 @@ impl<'a> PermissionsTest<'a> {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// TEST 4: Failure
|
||||
// TEST 5: Failure
|
||||
// Project has an organization
|
||||
// User affiliated with the project's org, with default failure permissions
|
||||
let test_4 = async {
|
||||
let test_5 = async {
|
||||
let (project_id, team_id) = create_dummy_project(test_env).await;
|
||||
let (organization_id, organization_team_id) = create_dummy_org(test_env).await;
|
||||
add_project_to_org(test_env, &project_id, &organization_id).await;
|
||||
@ -494,7 +568,7 @@ impl<'a> PermissionsTest<'a> {
|
||||
let resp = test_env.call(request).await;
|
||||
if !self.allowed_failure_codes.contains(&resp.status().as_u16()) {
|
||||
return Err(format!(
|
||||
"Test 4 failed. Expected failure codes {} got {}",
|
||||
"Test 5 failed. Expected failure codes {} got {}",
|
||||
self.allowed_failure_codes
|
||||
.iter()
|
||||
.map(|code| code.to_string())
|
||||
@ -507,7 +581,7 @@ impl<'a> PermissionsTest<'a> {
|
||||
get_project_permissions(self.user_id, self.user_pat, &project_id, test_env).await;
|
||||
if p != failure_project_permissions {
|
||||
return Err(format!(
|
||||
"Test 4 failed. Expected {:?}, got {:?}",
|
||||
"Test 5 failed. Expected {:?}, got {:?}",
|
||||
failure_project_permissions, p
|
||||
));
|
||||
}
|
||||
@ -515,55 +589,9 @@ impl<'a> PermissionsTest<'a> {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// TEST 5: Success
|
||||
// TEST 6: Success
|
||||
// Project has an organization
|
||||
// User affiliated with the project's org, with the default success
|
||||
let test_5 = async {
|
||||
let (project_id, team_id) = create_dummy_project(test_env).await;
|
||||
let (organization_id, organization_team_id) = create_dummy_org(test_env).await;
|
||||
add_project_to_org(test_env, &project_id, &organization_id).await;
|
||||
add_user_to_team(
|
||||
self.user_id,
|
||||
self.user_pat,
|
||||
&organization_team_id,
|
||||
Some(success_permissions),
|
||||
None,
|
||||
test_env,
|
||||
)
|
||||
.await;
|
||||
|
||||
let request = req_gen(&PermissionsTestContext {
|
||||
project_id: Some(&project_id),
|
||||
team_id: Some(&team_id),
|
||||
..test_context
|
||||
})
|
||||
.append_header(("Authorization", self.user_pat))
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(request).await;
|
||||
if !resp.status().is_success() {
|
||||
return Err(format!(
|
||||
"Test 5 failed. Expected success, got {}",
|
||||
resp.status().as_u16()
|
||||
));
|
||||
}
|
||||
|
||||
let p =
|
||||
get_project_permissions(self.user_id, self.user_pat, &project_id, test_env).await;
|
||||
if p != success_permissions {
|
||||
return Err(format!(
|
||||
"Test 5 failed. Expected {:?}, got {:?}",
|
||||
success_permissions, p
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// TEST 6: Failure
|
||||
// Project has an organization
|
||||
// User affiliated with the project's org (even can have successful permissions!)
|
||||
// User overwritten on the project team with failure permissions
|
||||
let test_6 = async {
|
||||
let (project_id, team_id) = create_dummy_project(test_env).await;
|
||||
let (organization_id, organization_team_id) = create_dummy_org(test_env).await;
|
||||
@ -577,6 +605,52 @@ impl<'a> PermissionsTest<'a> {
|
||||
test_env,
|
||||
)
|
||||
.await;
|
||||
|
||||
let request = req_gen(&PermissionsTestContext {
|
||||
project_id: Some(&project_id),
|
||||
team_id: Some(&team_id),
|
||||
..test_context
|
||||
})
|
||||
.append_header(("Authorization", self.user_pat))
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(request).await;
|
||||
if !resp.status().is_success() {
|
||||
return Err(format!(
|
||||
"Test 6 failed. Expected success, got {}",
|
||||
resp.status().as_u16()
|
||||
));
|
||||
}
|
||||
|
||||
let p =
|
||||
get_project_permissions(self.user_id, self.user_pat, &project_id, test_env).await;
|
||||
if p != success_permissions {
|
||||
return Err(format!(
|
||||
"Test 6 failed. Expected {:?}, got {:?}",
|
||||
success_permissions, p
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// TEST 7: Failure
|
||||
// Project has an organization
|
||||
// User affiliated with the project's org (even can have successful permissions!)
|
||||
// User overwritten on the project team with failure permissions
|
||||
let test_7 = async {
|
||||
let (project_id, team_id) = create_dummy_project(test_env).await;
|
||||
let (organization_id, organization_team_id) = create_dummy_org(test_env).await;
|
||||
add_project_to_org(test_env, &project_id, &organization_id).await;
|
||||
add_user_to_team(
|
||||
self.user_id,
|
||||
self.user_pat,
|
||||
&organization_team_id,
|
||||
Some(success_permissions),
|
||||
None,
|
||||
test_env,
|
||||
)
|
||||
.await;
|
||||
add_user_to_team(
|
||||
self.user_id,
|
||||
self.user_pat,
|
||||
@ -598,7 +672,7 @@ impl<'a> PermissionsTest<'a> {
|
||||
let resp = test_env.call(request).await;
|
||||
if !self.allowed_failure_codes.contains(&resp.status().as_u16()) {
|
||||
return Err(format!(
|
||||
"Test 6 failed. Expected failure codes {} got {}",
|
||||
"Test 7 failed. Expected failure codes {} got {}",
|
||||
self.allowed_failure_codes
|
||||
.iter()
|
||||
.map(|code| code.to_string())
|
||||
@ -611,7 +685,7 @@ impl<'a> PermissionsTest<'a> {
|
||||
get_project_permissions(self.user_id, self.user_pat, &project_id, test_env).await;
|
||||
if p != failure_project_permissions {
|
||||
return Err(format!(
|
||||
"Test 6 failed. Expected {:?}, got {:?}",
|
||||
"Test 7 failed. Expected {:?}, got {:?}",
|
||||
failure_project_permissions, p
|
||||
));
|
||||
}
|
||||
@ -619,11 +693,11 @@ impl<'a> PermissionsTest<'a> {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// TEST 7: Success
|
||||
// TEST 8: Success
|
||||
// Project has an organization
|
||||
// User affiliated with the project's org with default failure permissions
|
||||
// User overwritten to the project with the success permissions
|
||||
let test_7 = async {
|
||||
let test_8 = async {
|
||||
let (project_id, team_id) = create_dummy_project(test_env).await;
|
||||
let (organization_id, organization_team_id) = create_dummy_org(test_env).await;
|
||||
add_project_to_org(test_env, &project_id, &organization_id).await;
|
||||
@ -658,7 +732,7 @@ impl<'a> PermissionsTest<'a> {
|
||||
|
||||
if !resp.status().is_success() {
|
||||
return Err(format!(
|
||||
"Test 7 failed. Expected success, got {}",
|
||||
"Test 8 failed. Expected success, got {}",
|
||||
resp.status().as_u16()
|
||||
));
|
||||
}
|
||||
@ -667,7 +741,7 @@ impl<'a> PermissionsTest<'a> {
|
||||
get_project_permissions(self.user_id, self.user_pat, &project_id, test_env).await;
|
||||
if p != success_permissions {
|
||||
return Err(format!(
|
||||
"Test 7 failed. Expected {:?}, got {:?}",
|
||||
"Test 8 failed. Expected {:?}, got {:?}",
|
||||
success_permissions, p
|
||||
));
|
||||
}
|
||||
@ -675,7 +749,8 @@ impl<'a> PermissionsTest<'a> {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
tokio::try_join!(test_1, test_2, test_3, test_4, test_5, test_6, test_7,).map_err(|e| e)?;
|
||||
tokio::try_join!(test_1, test_2, test_3, test_4, test_5, test_6, test_7, test_8)
|
||||
.map_err(|e| e)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -845,7 +920,7 @@ impl<'a> PermissionsTest<'a> {
|
||||
}
|
||||
|
||||
async fn create_dummy_project(test_env: &TestEnvironment) -> (String, String) {
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
// Create a very simple project
|
||||
let slug = generate_random_name("test_project");
|
||||
@ -861,7 +936,7 @@ async fn create_dummy_project(test_env: &TestEnvironment) -> (String, String) {
|
||||
async fn create_dummy_org(test_env: &TestEnvironment) -> (String, String) {
|
||||
// Create a very simple organization
|
||||
let name = generate_random_name("test_org");
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let resp = api
|
||||
.create_organization(&name, "Example description.", ADMIN_USER_PAT)
|
||||
@ -878,7 +953,7 @@ async fn create_dummy_org(test_env: &TestEnvironment) -> (String, String) {
|
||||
}
|
||||
|
||||
async fn add_project_to_org(test_env: &TestEnvironment, project_id: &str, organization_id: &str) {
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
let resp = api
|
||||
.organization_add_project(organization_id, project_id, ADMIN_USER_PAT)
|
||||
.await;
|
||||
@ -893,7 +968,7 @@ async fn add_user_to_team(
|
||||
organization_permissions: Option<OrganizationPermissions>,
|
||||
test_env: &TestEnvironment,
|
||||
) {
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
// Invite user
|
||||
let resp = api
|
||||
@ -919,7 +994,7 @@ async fn modify_user_team_permissions(
|
||||
organization_permissions: Option<OrganizationPermissions>,
|
||||
test_env: &TestEnvironment,
|
||||
) {
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
// Send invitation to user
|
||||
let resp = api
|
||||
@ -938,7 +1013,7 @@ async fn modify_user_team_permissions(
|
||||
|
||||
async fn remove_user_from_team(user_id: &str, team_id: &str, test_env: &TestEnvironment) {
|
||||
// Send invitation to user
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
let resp = api.remove_from_team(team_id, user_id, ADMIN_USER_PAT).await;
|
||||
assert!(resp.status().is_success());
|
||||
}
|
||||
@ -949,7 +1024,7 @@ async fn get_project_permissions(
|
||||
project_id: &str,
|
||||
test_env: &TestEnvironment,
|
||||
) -> ProjectPermissions {
|
||||
let resp = test_env.v2.get_project_members(project_id, user_pat).await;
|
||||
let resp = test_env.v3.get_project_members(project_id, user_pat).await;
|
||||
let permissions = if resp.status().as_u16() == 200 {
|
||||
let value: serde_json::Value = test::read_body_json(resp).await;
|
||||
value
|
||||
@ -972,7 +1047,7 @@ async fn get_organization_permissions(
|
||||
organization_id: &str,
|
||||
test_env: &TestEnvironment,
|
||||
) -> OrganizationPermissions {
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
let resp = api
|
||||
.get_organization_members(organization_id, user_pat)
|
||||
.await;
|
||||
|
||||
@ -32,6 +32,19 @@ INSERT INTO loader_field_enum_values (enum_id, value) SELECT id, 'forge' FROM lo
|
||||
INSERT INTO loaders_project_types_games (loader_id, project_type_id, game_id) SELECT joining_loader_id, joining_project_type_id, 1 FROM loaders_project_types WHERE joining_loader_id = 5;
|
||||
INSERT INTO loaders_project_types_games (loader_id, project_type_id, game_id) SELECT joining_loader_id, joining_project_type_id, 1 FROM loaders_project_types WHERE joining_loader_id = 6;
|
||||
|
||||
-- Dummy-data only optional field, as we don't have any yet
|
||||
INSERT INTO loader_fields (
|
||||
field,
|
||||
field_type,
|
||||
optional
|
||||
) VALUES (
|
||||
'test_fabric_optional',
|
||||
'integer',
|
||||
true
|
||||
);
|
||||
INSERT INTO loader_fields_loaders(loader_id, loader_field_id)
|
||||
SELECT l.id, lf.id FROM loaders l CROSS JOIN loader_fields lf WHERE lf.field = 'test_fabric_optional' AND l.loader = 'fabric';
|
||||
|
||||
-- Sample game versions, loaders, categories
|
||||
-- Game versions is '2'
|
||||
INSERT INTO loader_field_enum_values(enum_id, value, metadata, created)
|
||||
|
||||
306
tests/loader_fields.rs
Normal file
306
tests/loader_fields.rs
Normal file
@ -0,0 +1,306 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use common::environment::TestEnvironment;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::api_v3::request_data::get_public_version_creation_data;
|
||||
use crate::common::database::*;
|
||||
|
||||
use crate::common::dummy_data::TestFile;
|
||||
|
||||
// importing common module.
|
||||
mod common;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn creating_loader_fields() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let alpha_project_id = &test_env
|
||||
.dummy
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.project_alpha
|
||||
.project_id
|
||||
.clone();
|
||||
let alpha_project_id = serde_json::from_str(&format!("\"{}\"", alpha_project_id)).unwrap();
|
||||
let alpha_version_id = &test_env
|
||||
.dummy
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.project_alpha
|
||||
.version_id
|
||||
.clone();
|
||||
|
||||
// ALL THE FOLLOWING FOR CREATE AND PATCH
|
||||
// Cannot create a version with an extra argument that cannot be tied to a loader field ("invalid loader field")
|
||||
// TODO: - Create project
|
||||
// - Create version
|
||||
let version_data = get_public_version_creation_data(
|
||||
alpha_project_id,
|
||||
"1.0.0",
|
||||
TestFile::build_random_jar(),
|
||||
Some(|j: &mut serde_json::Value| {
|
||||
j["invalid"] = json!("invalid");
|
||||
}),
|
||||
);
|
||||
let resp = api.add_public_version(version_data, USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
// - Patch
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"invalid": "invalid"
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Cannot create a version with a loader field that isnt used by the loader
|
||||
// TODO: - Create project
|
||||
// - Create version
|
||||
let version_data = get_public_version_creation_data(
|
||||
alpha_project_id,
|
||||
"1.0.0",
|
||||
TestFile::build_random_jar(),
|
||||
Some(|j: &mut serde_json::Value| {
|
||||
// This is only for mrpacks, not mods/jars
|
||||
j["mrpack_loaders"] = json!(["fabric"]);
|
||||
}),
|
||||
);
|
||||
let resp = api.add_public_version(version_data, USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
// - Patch
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"mrpack_loaders": ["fabric"]
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Cannot create a version without an applicable loader field that is not optional
|
||||
// TODO: - Create project
|
||||
// - Create version
|
||||
let version_data = get_public_version_creation_data(
|
||||
alpha_project_id,
|
||||
"1.0.0",
|
||||
TestFile::build_random_jar(),
|
||||
Some(|j: &mut serde_json::Value| {
|
||||
let j = j.as_object_mut().unwrap();
|
||||
j.remove("client_side");
|
||||
}),
|
||||
);
|
||||
let resp = api.add_public_version(version_data, USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Cannot create a version without a loader field array that has a minimum of 1
|
||||
// TODO: - Create project
|
||||
// - Create version
|
||||
let version_data = get_public_version_creation_data(
|
||||
alpha_project_id,
|
||||
"1.0.0",
|
||||
TestFile::build_random_jar(),
|
||||
Some(|j: &mut serde_json::Value| {
|
||||
let j = j.as_object_mut().unwrap();
|
||||
j.remove("game_versions");
|
||||
}),
|
||||
);
|
||||
let resp = api.add_public_version(version_data, USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// TODO: Create a test for too many elements in the array when we have a LF that has a max (past max)
|
||||
// Cannot create a version with a loader field array that has fewer than the minimum elements
|
||||
// TODO: - Create project
|
||||
// - Create version
|
||||
let version_data = get_public_version_creation_data(
|
||||
alpha_project_id,
|
||||
"1.0.0",
|
||||
TestFile::build_random_jar(),
|
||||
Some(|j: &mut serde_json::Value| {
|
||||
let j: &mut serde_json::Map<String, serde_json::Value> = j.as_object_mut().unwrap();
|
||||
j["game_versions"] = json!([]);
|
||||
}),
|
||||
);
|
||||
let resp: actix_web::dev::ServiceResponse =
|
||||
api.add_public_version(version_data, USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// - Patch
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"game_versions": []
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Cannot create an invalid data type for the loader field type (including bad variant for the type)
|
||||
for bad_type_game_versions in [
|
||||
json!(1),
|
||||
json!([1]),
|
||||
json!("1.20.1"),
|
||||
json!(["client_side"]),
|
||||
] {
|
||||
// TODO: - Create project
|
||||
// - Create version
|
||||
let version_data = get_public_version_creation_data(
|
||||
alpha_project_id,
|
||||
"1.0.0",
|
||||
TestFile::build_random_jar(),
|
||||
Some(|j: &mut serde_json::Value| {
|
||||
let j: &mut serde_json::Map<String, serde_json::Value> = j.as_object_mut().unwrap();
|
||||
j["game_versions"] = bad_type_game_versions.clone();
|
||||
}),
|
||||
);
|
||||
let resp = api.add_public_version(version_data, USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// - Patch
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"game_versions": bad_type_game_versions
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
}
|
||||
|
||||
// Can create with optional loader fields (other tests have checked if we can create without them)
|
||||
// TODO: - Create project
|
||||
// - Create version
|
||||
let version_data = get_public_version_creation_data(
|
||||
alpha_project_id,
|
||||
"1.0.0",
|
||||
TestFile::build_random_jar(),
|
||||
Some(|j: &mut serde_json::Value| {
|
||||
j["test_fabric_optional"] = json!(555);
|
||||
}),
|
||||
);
|
||||
let v = api
|
||||
.add_public_version_deserialized(version_data, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(v.fields.get("test_fabric_optional").unwrap(), &json!(555));
|
||||
// - Patch
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"test_fabric_optional": 555
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
let v = api
|
||||
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(v.fields.get("test_fabric_optional").unwrap(), &json!(555));
|
||||
|
||||
// Simply setting them as expected works
|
||||
// - Create
|
||||
let version_data = get_public_version_creation_data(
|
||||
alpha_project_id,
|
||||
"1.0.0",
|
||||
TestFile::build_random_jar(),
|
||||
Some(|j: &mut serde_json::Value| {
|
||||
let j: &mut serde_json::Map<String, serde_json::Value> = j.as_object_mut().unwrap();
|
||||
j["game_versions"] = json!(["1.20.1", "1.20.2"]);
|
||||
j["client_side"] = json!("optional");
|
||||
j["server_side"] = json!("required");
|
||||
}),
|
||||
);
|
||||
let v = api
|
||||
.add_public_version_deserialized(version_data, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(
|
||||
v.fields.get("game_versions").unwrap(),
|
||||
&json!(["1.20.1", "1.20.2"])
|
||||
);
|
||||
assert_eq!(v.fields.get("client_side").unwrap(), &json!("optional"));
|
||||
assert_eq!(v.fields.get("server_side").unwrap(), &json!("required"));
|
||||
// - Patch
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"game_versions": ["1.20.1", "1.20.2"],
|
||||
"client_side": "optional",
|
||||
"server_side": "required"
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
let v = api
|
||||
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(
|
||||
v.fields.get("game_versions").unwrap(),
|
||||
&json!(["1.20.1", "1.20.2"])
|
||||
);
|
||||
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn get_loader_fields() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let game_versions = api
|
||||
.get_loader_field_variants_deserialized("game_versions")
|
||||
.await;
|
||||
let side_types = api
|
||||
.get_loader_field_variants_deserialized("client_side")
|
||||
.await;
|
||||
|
||||
// These tests match dummy data and will need to be updated if the dummy data changes
|
||||
// Versions should be ordered by:
|
||||
// - ordering
|
||||
// - ordering ties settled by date added to database
|
||||
// - We also expect presentation of NEWEST to OLDEST
|
||||
// - All null orderings are treated as older than any non-null ordering
|
||||
// (for this test, the 1.20.1, etc, versions are all null ordering)
|
||||
let game_version_versions = game_versions
|
||||
.into_iter()
|
||||
.map(|x| x.value)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
game_version_versions,
|
||||
[
|
||||
"Ordering_Negative1",
|
||||
"Ordering_Positive100",
|
||||
"1.20.5",
|
||||
"1.20.4",
|
||||
"1.20.3",
|
||||
"1.20.2",
|
||||
"1.20.1"
|
||||
]
|
||||
);
|
||||
|
||||
let side_type_names = side_types
|
||||
.into_iter()
|
||||
.map(|x| x.value)
|
||||
.collect::<HashSet<_>>();
|
||||
assert_eq!(
|
||||
side_type_names,
|
||||
["unknown", "required", "optional", "unsupported"]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
);
|
||||
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
@ -15,7 +15,7 @@ pub async fn get_user_notifications_after_team_invitation_returns_notification()
|
||||
.project_alpha
|
||||
.team_id
|
||||
.clone();
|
||||
let api = test_env.v2;
|
||||
let api = test_env.v3;
|
||||
api.get_user_notifications_deserialized(FRIEND_USER_ID, FRIEND_USER_PAT)
|
||||
.await;
|
||||
|
||||
@ -34,7 +34,7 @@ pub async fn get_user_notifications_after_team_invitation_returns_notification()
|
||||
pub async fn get_user_notifications_after_reading_indicates_notification_read() {
|
||||
with_test_environment(|test_env| async move {
|
||||
test_env.generate_friend_user_notification().await;
|
||||
let api = test_env.v2;
|
||||
let api = test_env.v3;
|
||||
let notifications = api
|
||||
.get_user_notifications_deserialized(FRIEND_USER_ID, FRIEND_USER_PAT)
|
||||
.await;
|
||||
@ -57,7 +57,7 @@ pub async fn get_user_notifications_after_reading_indicates_notification_read()
|
||||
pub async fn get_user_notifications_after_deleting_does_not_show_notification() {
|
||||
with_test_environment(|test_env| async move {
|
||||
test_env.generate_friend_user_notification().await;
|
||||
let api = test_env.v2;
|
||||
let api = test_env.v3;
|
||||
let notifications = api
|
||||
.get_user_notifications_deserialized(FRIEND_USER_ID, FRIEND_USER_PAT)
|
||||
.await;
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
use crate::common::{
|
||||
api_v3::oauth::get_redirect_location_query_params, database::FRIEND_USER_ID,
|
||||
dummy_data::DummyOAuthClientAlpha,
|
||||
};
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::test::{self};
|
||||
use actix_web::test;
|
||||
use common::{
|
||||
api_v3::oauth::get_redirect_location_query_params,
|
||||
api_v3::oauth::{get_auth_code_from_redirect_params, get_authorize_accept_flow_id},
|
||||
asserts::{assert_any_status_except, assert_status},
|
||||
database::FRIEND_USER_ID,
|
||||
database::{FRIEND_USER_PAT, USER_USER_ID, USER_USER_PAT},
|
||||
dummy_data::DummyOAuthClientAlpha,
|
||||
environment::with_test_environment,
|
||||
};
|
||||
use labrinth::auth::oauth::TokenResponse;
|
||||
|
||||
@ -14,7 +14,7 @@ use labrinth::{
|
||||
routes::v3::oauth_clients::OAuthClientEdit,
|
||||
};
|
||||
|
||||
use crate::common::{asserts::assert_status, database::USER_USER_ID_PARSED};
|
||||
use common::{asserts::assert_status, database::USER_USER_ID_PARSED};
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use crate::common::{
|
||||
api_v3::request_data::get_icon_data,
|
||||
database::{generate_random_name, ADMIN_USER_PAT, MOD_USER_ID, MOD_USER_PAT, USER_USER_ID},
|
||||
dummy_data::DummyImage,
|
||||
environment::TestEnvironment,
|
||||
request_data::get_icon_data,
|
||||
};
|
||||
use actix_web::test;
|
||||
use bytes::Bytes;
|
||||
@ -18,7 +18,7 @@ mod common;
|
||||
#[actix_rt::test]
|
||||
async fn create_organization() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
let zeta_organization_slug = &test_env
|
||||
.dummy
|
||||
.as_ref()
|
||||
@ -86,7 +86,7 @@ async fn create_organization() {
|
||||
#[actix_rt::test]
|
||||
async fn patch_organization() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let zeta_organization_id = &test_env
|
||||
.dummy
|
||||
@ -168,7 +168,7 @@ async fn patch_organization() {
|
||||
#[actix_rt::test]
|
||||
async fn add_remove_icon() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
let zeta_organization_id = &test_env
|
||||
.dummy
|
||||
.as_ref()
|
||||
@ -178,7 +178,7 @@ async fn add_remove_icon() {
|
||||
|
||||
// Get project
|
||||
let resp = test_env
|
||||
.v2
|
||||
.v3
|
||||
.get_organization_deserialized(zeta_organization_id, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.icon_url, None);
|
||||
@ -220,7 +220,7 @@ async fn add_remove_icon() {
|
||||
#[actix_rt::test]
|
||||
async fn delete_org() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
let zeta_organization_id = &test_env
|
||||
.dummy
|
||||
.as_ref()
|
||||
@ -258,14 +258,14 @@ async fn add_remove_organization_projects() {
|
||||
// Add/remove project to organization, first by ID, then by slug
|
||||
for alpha in [alpha_project_id, alpha_project_slug] {
|
||||
let resp = test_env
|
||||
.v2
|
||||
.v3
|
||||
.organization_add_project(zeta_organization_id, alpha, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
// Get organization projects
|
||||
let projects = test_env
|
||||
.v2
|
||||
.v3
|
||||
.get_organization_projects_deserialized(zeta_organization_id, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(projects[0].id.to_string(), alpha_project_id);
|
||||
@ -273,14 +273,14 @@ async fn add_remove_organization_projects() {
|
||||
|
||||
// Remove project from organization
|
||||
let resp = test_env
|
||||
.v2
|
||||
.v3
|
||||
.organization_remove_project(zeta_organization_id, alpha, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
// Get organization projects
|
||||
let projects = test_env
|
||||
.v2
|
||||
.v3
|
||||
.get_organization_projects_deserialized(zeta_organization_id, USER_USER_PAT)
|
||||
.await;
|
||||
assert!(projects.is_empty());
|
||||
@ -304,7 +304,7 @@ async fn permissions_patch_organization() {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/organization/{}",
|
||||
"/v3/organization/{}",
|
||||
ctx.organization_id.unwrap()
|
||||
))
|
||||
.set_json(json!({
|
||||
@ -344,7 +344,7 @@ async fn permissions_edit_details() {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/organization/{}/icon?ext=png",
|
||||
"/v3/organization/{}/icon?ext=png",
|
||||
ctx.organization_id.unwrap()
|
||||
))
|
||||
.set_payload(Bytes::from(
|
||||
@ -362,7 +362,7 @@ async fn permissions_edit_details() {
|
||||
// Uses alpha project to delete added icon
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/organization/{}/icon?ext=png",
|
||||
"/v3/organization/{}/icon?ext=png",
|
||||
ctx.organization_id.unwrap()
|
||||
))
|
||||
};
|
||||
@ -378,7 +378,7 @@ async fn permissions_edit_details() {
|
||||
async fn permissions_manage_invites() {
|
||||
// Add member, remove member, edit member
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let zeta_organization_id = &test_env
|
||||
.dummy
|
||||
@ -393,7 +393,7 @@ async fn permissions_manage_invites() {
|
||||
// Add member
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{}/members", ctx.team_id.unwrap()))
|
||||
.uri(&format!("/v3/team/{}/members", ctx.team_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"user_id": MOD_USER_ID,
|
||||
"permissions": 0,
|
||||
@ -412,7 +412,7 @@ async fn permissions_manage_invites() {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/team/{}/members/{MOD_USER_ID}",
|
||||
"/v3/team/{}/members/{MOD_USER_ID}",
|
||||
ctx.team_id.unwrap()
|
||||
))
|
||||
.set_json(json!({
|
||||
@ -430,7 +430,7 @@ async fn permissions_manage_invites() {
|
||||
// requires manage_invites if they have not yet accepted the invite
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/team/{}/members/{MOD_USER_ID}",
|
||||
"/v3/team/{}/members/{MOD_USER_ID}",
|
||||
ctx.team_id.unwrap()
|
||||
))
|
||||
};
|
||||
@ -453,7 +453,7 @@ async fn permissions_manage_invites() {
|
||||
let remove_member = OrganizationPermissions::REMOVE_MEMBER;
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/team/{}/members/{MOD_USER_ID}",
|
||||
"/v3/team/{}/members/{MOD_USER_ID}",
|
||||
ctx.team_id.unwrap()
|
||||
))
|
||||
};
|
||||
@ -471,7 +471,7 @@ async fn permissions_manage_invites() {
|
||||
#[actix_rt::test]
|
||||
async fn permissions_add_remove_project() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id;
|
||||
let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id;
|
||||
@ -503,7 +503,7 @@ async fn permissions_add_remove_project() {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!(
|
||||
"/v2/organization/{}/projects",
|
||||
"/v3/organization/{}/projects",
|
||||
ctx.organization_id.unwrap()
|
||||
))
|
||||
.set_json(json!({
|
||||
@ -521,7 +521,7 @@ async fn permissions_add_remove_project() {
|
||||
let remove_project = OrganizationPermissions::REMOVE_PROJECT;
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/organization/{}/projects/{alpha_project_id}",
|
||||
"/v3/organization/{}/projects/{alpha_project_id}",
|
||||
ctx.organization_id.unwrap()
|
||||
))
|
||||
};
|
||||
@ -544,7 +544,7 @@ async fn permissions_delete_organization() {
|
||||
// Add alpha project to zeta organization
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/organization/{}",
|
||||
"/v3/organization/{}",
|
||||
ctx.organization_id.unwrap()
|
||||
))
|
||||
};
|
||||
@ -578,10 +578,10 @@ async fn permissions_add_default_project_permissions() {
|
||||
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{}/members", ctx.team_id.unwrap()))
|
||||
.uri(&format!("/v3/team/{}/members", ctx.team_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"user_id": MOD_USER_ID,
|
||||
// do not set permissions as it will be set to default, which is non-zero
|
||||
"permissions": (ProjectPermissions::UPLOAD_VERSION | ProjectPermissions::DELETE_VERSION).bits(),
|
||||
"organization_permissions": 0,
|
||||
}))
|
||||
};
|
||||
@ -606,7 +606,7 @@ async fn permissions_add_default_project_permissions() {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/team/{}/members/{MOD_USER_ID}",
|
||||
"/v3/team/{}/members/{MOD_USER_ID}",
|
||||
ctx.team_id.unwrap()
|
||||
))
|
||||
.set_json(json!({
|
||||
@ -633,7 +633,7 @@ async fn permissions_organization_permissions_consistency_test() {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/organization/{}",
|
||||
"/v3/organization/{}",
|
||||
ctx.organization_id.unwrap()
|
||||
))
|
||||
.set_json(json!({
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
use actix_web::test;
|
||||
use chrono::{Duration, Utc};
|
||||
use common::database::*;
|
||||
use common::environment::TestEnvironment;
|
||||
use labrinth::models::pats::Scopes;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::environment::TestEnvironment;
|
||||
|
||||
// importing common module.
|
||||
mod common;
|
||||
|
||||
// Full pat test:
|
||||
@ -22,7 +20,7 @@ pub async fn pat_full_test() {
|
||||
|
||||
// Create a PAT for a full test
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/pat")
|
||||
.uri("/v3/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
||||
@ -46,7 +44,7 @@ pub async fn pat_full_test() {
|
||||
// Get PAT again
|
||||
let req = test::TestRequest::get()
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.uri("/v2/pat")
|
||||
.uri("/v3/pat")
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status().as_u16(), 200);
|
||||
@ -62,7 +60,7 @@ pub async fn pat_full_test() {
|
||||
let token = token.to_string();
|
||||
async {
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/collection")
|
||||
.uri("/v3/collection")
|
||||
.append_header(("Authorization", token))
|
||||
.set_json(json!({
|
||||
"title": "Test Collection 1",
|
||||
@ -78,7 +76,7 @@ pub async fn pat_full_test() {
|
||||
|
||||
// Change scopes and test again
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/pat/{}", id))
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": 0,
|
||||
@ -90,7 +88,7 @@ pub async fn pat_full_test() {
|
||||
|
||||
// Change scopes back, and set expiry to the past, and test again
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/pat/{}", id))
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": Scopes::COLLECTION_CREATE,
|
||||
@ -106,7 +104,7 @@ pub async fn pat_full_test() {
|
||||
|
||||
// Change everything back to normal and test again
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/pat/{}", id))
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"expires": Utc::now() + Duration::days(1), // no longer expired!
|
||||
@ -118,7 +116,7 @@ pub async fn pat_full_test() {
|
||||
|
||||
// Patching to a bad expiry should fail
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/pat/{}", id))
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"expires": Utc::now() - Duration::days(1), // Past
|
||||
@ -135,7 +133,7 @@ pub async fn pat_full_test() {
|
||||
}
|
||||
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/pat/{}", id))
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": scope.bits(),
|
||||
@ -151,7 +149,7 @@ pub async fn pat_full_test() {
|
||||
// Delete PAT
|
||||
let req = test::TestRequest::delete()
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.uri(&format!("/v2/pat/{}", id))
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status().as_u16(), 204);
|
||||
@ -167,7 +165,7 @@ pub async fn bad_pats() {
|
||||
|
||||
// Creating a PAT with no name should fail
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/pat")
|
||||
.uri("/v3/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
||||
@ -180,7 +178,7 @@ pub async fn bad_pats() {
|
||||
// Name too short or too long should fail
|
||||
for name in ["n", "this_name_is_too_long".repeat(16).as_str()] {
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/pat")
|
||||
.uri("/v3/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"name": name,
|
||||
@ -194,7 +192,7 @@ pub async fn bad_pats() {
|
||||
|
||||
// Creating a PAT with an expiry in the past should fail
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/pat")
|
||||
.uri("/v3/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
||||
@ -212,7 +210,7 @@ pub async fn bad_pats() {
|
||||
continue;
|
||||
}
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/pat")
|
||||
.uri("/v3/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": scope.bits(),
|
||||
@ -229,7 +227,7 @@ pub async fn bad_pats() {
|
||||
|
||||
// Create a 'good' PAT for patching
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/pat")
|
||||
.uri("/v3/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": Scopes::COLLECTION_CREATE,
|
||||
@ -245,7 +243,7 @@ pub async fn bad_pats() {
|
||||
// Patching to a bad name should fail
|
||||
for name in ["n", "this_name_is_too_long".repeat(16).as_str()] {
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/pat")
|
||||
.uri("/v3/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"name": name,
|
||||
@ -257,7 +255,7 @@ pub async fn bad_pats() {
|
||||
|
||||
// Patching to a bad expiry should fail
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/pat/{}", id))
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"expires": Utc::now() - Duration::days(1), // Past
|
||||
@ -274,7 +272,7 @@ pub async fn bad_pats() {
|
||||
}
|
||||
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/pat/{}", id))
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": scope.bits(),
|
||||
|
||||
183
tests/project.rs
183
tests/project.rs
@ -2,21 +2,17 @@ use actix_http::StatusCode;
|
||||
use actix_web::test;
|
||||
use bytes::Bytes;
|
||||
use chrono::{Duration, Utc};
|
||||
use common::database::*;
|
||||
use common::dummy_data::DUMMY_CATEGORIES;
|
||||
use common::environment::{with_test_environment, TestEnvironment};
|
||||
use common::permissions::{PermissionsTest, PermissionsTestContext};
|
||||
use futures::StreamExt;
|
||||
use itertools::Itertools;
|
||||
use labrinth::database::models::project_item::{PROJECTS_NAMESPACE, PROJECTS_SLUGS_NAMESPACE};
|
||||
use labrinth::models::ids::base62_impl::parse_base62;
|
||||
use labrinth::models::teams::ProjectPermissions;
|
||||
use labrinth::util::actix::{AppendsMultipart, MultipartSegment, MultipartSegmentData};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::{database::*, request_data};
|
||||
|
||||
use crate::common::dummy_data::{TestFile, DUMMY_CATEGORIES};
|
||||
|
||||
// importing common module.
|
||||
mod common;
|
||||
|
||||
#[actix_rt::test]
|
||||
@ -30,7 +26,7 @@ async fn test_get_project() {
|
||||
|
||||
// Perform request on dummy data
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/project/{alpha_project_id}"))
|
||||
.uri(&format!("/v3/project/{alpha_project_id}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -66,7 +62,7 @@ async fn test_get_project() {
|
||||
|
||||
// Make the request again, this time it should be cached
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/project/{alpha_project_id}"))
|
||||
.uri(&format!("/v3/project/{alpha_project_id}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -79,7 +75,7 @@ async fn test_get_project() {
|
||||
|
||||
// Request should fail on non-existent project
|
||||
let req = test::TestRequest::get()
|
||||
.uri("/v2/project/nonexistent")
|
||||
.uri("/v3/project/nonexistent")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
|
||||
@ -88,7 +84,7 @@ async fn test_get_project() {
|
||||
|
||||
// Similarly, request should fail on non-authorized user, on a yet-to-be-approved or hidden project, with a 404 (hiding the existence of the project)
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/project/{beta_project_id}"))
|
||||
.uri(&format!("/v3/project/{beta_project_id}"))
|
||||
.append_header(("Authorization", ENEMY_USER_PAT))
|
||||
.to_request();
|
||||
|
||||
@ -103,7 +99,7 @@ async fn test_get_project() {
|
||||
async fn test_add_remove_project() {
|
||||
// Test setup and dummy data
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
// Generate test project data.
|
||||
let mut json_data = json!(
|
||||
@ -112,14 +108,14 @@ async fn test_add_remove_project() {
|
||||
"slug": "demo",
|
||||
"description": "Example description.",
|
||||
"body": "Example body.",
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"initial_versions": [{
|
||||
"file_parts": ["basic-mod.jar"],
|
||||
"version_number": "1.2.3",
|
||||
"version_title": "start",
|
||||
"dependencies": [],
|
||||
"game_versions": ["1.20.1"] ,
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"release_channel": "release",
|
||||
"loaders": ["fabric"],
|
||||
"featured": true
|
||||
@ -157,6 +153,7 @@ async fn test_add_remove_project() {
|
||||
name: "basic-mod.jar".to_string(),
|
||||
filename: Some("basic-mod.jar".to_string()),
|
||||
content_type: Some("application/java-archive".to_string()),
|
||||
// TODO: look at these: can be used in the reuse data
|
||||
data: MultipartSegmentData::Binary(include_bytes!("../tests/files/basic-mod.jar").to_vec()),
|
||||
};
|
||||
|
||||
@ -180,7 +177,7 @@ async fn test_add_remove_project() {
|
||||
|
||||
// Add a project- simple, should work.
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
|
||||
.to_request();
|
||||
@ -206,7 +203,7 @@ async fn test_add_remove_project() {
|
||||
// Reusing with a different slug and the same file should fail
|
||||
// Even if that file is named differently
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![
|
||||
json_diff_slug_file_segment.clone(), // Different slug, different file name
|
||||
@ -219,7 +216,7 @@ async fn test_add_remove_project() {
|
||||
|
||||
// Reusing with the same slug and a different file should fail
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![
|
||||
json_diff_file_segment.clone(), // Same slug, different file name
|
||||
@ -232,7 +229,7 @@ async fn test_add_remove_project() {
|
||||
|
||||
// Different slug, different file should succeed
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![
|
||||
json_diff_slug_file_segment.clone(), // Different slug, different file name
|
||||
@ -248,7 +245,7 @@ async fn test_add_remove_project() {
|
||||
let id = project.id.to_string();
|
||||
|
||||
// Remove the project
|
||||
let resp = test_env.v2.remove_project("demo", USER_USER_PAT).await;
|
||||
let resp = test_env.v3.remove_project("demo", USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Confirm that the project is gone from the cache
|
||||
@ -279,59 +276,10 @@ async fn test_add_remove_project() {
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_project_type_sanity() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
|
||||
// Perform all other patch tests on both 'mod' and 'modpack'
|
||||
let test_creation_mod = request_data::get_public_project_creation_data(
|
||||
"test-mod",
|
||||
Some(TestFile::build_random_jar()),
|
||||
);
|
||||
let test_creation_modpack = request_data::get_public_project_creation_data(
|
||||
"test-modpack",
|
||||
Some(TestFile::build_random_mrpack()),
|
||||
);
|
||||
for (mod_or_modpack, test_creation_data) in [
|
||||
("mod", test_creation_mod),
|
||||
("modpack", test_creation_modpack),
|
||||
] {
|
||||
let (test_project, test_version) = api
|
||||
.add_public_project(test_creation_data, USER_USER_PAT)
|
||||
.await;
|
||||
let test_project_slug = test_project.slug.as_ref().unwrap();
|
||||
|
||||
assert_eq!(test_project.project_type, mod_or_modpack);
|
||||
assert_eq!(test_project.loaders, vec!["fabric"]);
|
||||
assert_eq!(
|
||||
test_version[0].loaders.iter().map(|x| &x.0).collect_vec(),
|
||||
vec!["fabric"]
|
||||
);
|
||||
|
||||
let project = api
|
||||
.get_project_deserialized(test_project_slug, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(test_project.loaders, vec!["fabric"]);
|
||||
assert_eq!(project.project_type, mod_or_modpack);
|
||||
|
||||
let version = api
|
||||
.get_version_deserialized(&test_version[0].id.to_string(), USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(
|
||||
version.loaders.iter().map(|x| &x.0).collect_vec(),
|
||||
vec!["fabric"]
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: as we get more complicated strucures with v3 testing, and alpha/beta get more complicated, we should add more tests here,
|
||||
// to ensure that projects created with v3 routes are still valid and work with v2 routes.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
pub async fn test_patch_project() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let alpha_project_slug = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug;
|
||||
let beta_project_slug = &test_env.dummy.as_ref().unwrap().project_beta.project_slug;
|
||||
@ -453,8 +401,6 @@ pub async fn test_patch_project() {
|
||||
"issues_url": "https://github.com",
|
||||
"discord_url": "https://discord.gg",
|
||||
"wiki_url": "https://wiki.com",
|
||||
"client_side": "optional",
|
||||
"server_side": "required",
|
||||
"donation_urls": [{
|
||||
"id": "patreon",
|
||||
"platform": "Patreon",
|
||||
@ -471,30 +417,21 @@ pub async fn test_patch_project() {
|
||||
assert_eq!(resp.status(), 404);
|
||||
|
||||
// New slug does work
|
||||
let resp = api.get_project("newslug", USER_USER_PAT).await;
|
||||
let project: serde_json::Value = test::read_body_json(resp).await;
|
||||
let project = api.get_project_deserialized("newslug", USER_USER_PAT).await;
|
||||
|
||||
assert_eq!(project["slug"], json!(Some("newslug".to_string())));
|
||||
assert_eq!(project["title"], "New successful title");
|
||||
assert_eq!(project["description"], "New successful description");
|
||||
assert_eq!(project["body"], "New successful body");
|
||||
assert_eq!(project["categories"], json!(vec![DUMMY_CATEGORIES[0]]));
|
||||
assert_eq!(project["license"]["id"], "MIT");
|
||||
assert_eq!(
|
||||
project["issues_url"],
|
||||
json!(Some("https://github.com".to_string()))
|
||||
);
|
||||
assert_eq!(
|
||||
project["discord_url"],
|
||||
json!(Some("https://discord.gg".to_string()))
|
||||
);
|
||||
assert_eq!(
|
||||
project["wiki_url"],
|
||||
json!(Some("https://wiki.com".to_string()))
|
||||
);
|
||||
assert_eq!(project["client_side"], json!("optional"));
|
||||
assert_eq!(project["server_side"], json!("required"));
|
||||
assert_eq!(project["donation_urls"][0]["url"], "https://patreon.com");
|
||||
assert_eq!(project.slug.unwrap(), "newslug");
|
||||
assert_eq!(project.title, "New successful title");
|
||||
assert_eq!(project.description, "New successful description");
|
||||
assert_eq!(project.body, "New successful body");
|
||||
assert_eq!(project.categories, vec![DUMMY_CATEGORIES[0]]);
|
||||
assert_eq!(project.license.id, "MIT");
|
||||
assert_eq!(project.issues_url, Some("https://github.com".to_string()));
|
||||
assert_eq!(project.discord_url, Some("https://discord.gg".to_string()));
|
||||
assert_eq!(project.wiki_url, Some("https://wiki.com".to_string()));
|
||||
assert_eq!(project.donation_urls.unwrap()[0].url, "https://patreon.com");
|
||||
|
||||
// TODO:
|
||||
// for loader fields?
|
||||
|
||||
// Cleanup test db
|
||||
test_env.cleanup().await;
|
||||
@ -503,7 +440,7 @@ pub async fn test_patch_project() {
|
||||
#[actix_rt::test]
|
||||
pub async fn test_bulk_edit_categories() {
|
||||
with_test_environment(|test_env| async move {
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id;
|
||||
let beta_project_id: &str = &test_env.dummy.as_ref().unwrap().project_beta.project_id;
|
||||
|
||||
@ -555,8 +492,6 @@ async fn permissions_patch_project() {
|
||||
("title", json!("randomname")),
|
||||
("description", json!("randomdescription")),
|
||||
("categories", json!(["combat", "economy"])),
|
||||
// ("client_side", json!("unsupported")),
|
||||
// ("server_side", json!("unsupported")),
|
||||
("additional_categories", json!(["decoration"])),
|
||||
("issues_url", json!("https://issues.com")),
|
||||
("source_url", json!("https://source.com")),
|
||||
@ -579,7 +514,7 @@ async fn permissions_patch_project() {
|
||||
async move {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{}", ctx.project_id.unwrap()))
|
||||
.uri(&format!("/v3/project/{}", ctx.project_id.unwrap()))
|
||||
.set_json(json!({
|
||||
key: if key == "slug" {
|
||||
json!(generate_random_name("randomslug"))
|
||||
@ -602,7 +537,7 @@ async fn permissions_patch_project() {
|
||||
// This requires a project with a version, so we use alpha_project_id
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{}", ctx.project_id.unwrap()))
|
||||
.uri(&format!("/v3/project/{}", ctx.project_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"status": "private",
|
||||
"requested_status": "private",
|
||||
@ -619,7 +554,7 @@ async fn permissions_patch_project() {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/projects?ids=[{uri}]",
|
||||
"/v3/projects?ids=[{uri}]",
|
||||
uri = urlencoding::encode(&format!("\"{}\"", ctx.project_id.unwrap()))
|
||||
))
|
||||
.set_json(json!({
|
||||
@ -636,7 +571,7 @@ async fn permissions_patch_project() {
|
||||
let edit_body = ProjectPermissions::EDIT_BODY;
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{}", ctx.project_id.unwrap()))
|
||||
.uri(&format!("/v3/project/{}", ctx.project_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"body": "new body!",
|
||||
}))
|
||||
@ -663,7 +598,7 @@ async fn permissions_edit_details() {
|
||||
|
||||
// Approve beta version as private so we can schedule it
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/version/{beta_version_id}"))
|
||||
.uri(&format!("/v3/version/{beta_version_id}"))
|
||||
.append_header(("Authorization", MOD_USER_PAT))
|
||||
.set_json(json!({
|
||||
"status": "unlisted"
|
||||
@ -675,7 +610,7 @@ async fn permissions_edit_details() {
|
||||
// Schedule version
|
||||
let req_gen = |_: &PermissionsTestContext| {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/version/{beta_version_id}/schedule")) // beta_version_id is an *approved* version, so we can schedule it
|
||||
.uri(&format!("/v3/version/{beta_version_id}/schedule")) // beta_version_id is an *approved* version, so we can schedule it
|
||||
.set_json(json!(
|
||||
{
|
||||
"requested_status": "archived",
|
||||
@ -695,7 +630,7 @@ async fn permissions_edit_details() {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/project/{}/icon?ext=png",
|
||||
"/v3/project/{}/icon?ext=png",
|
||||
ctx.project_id.unwrap()
|
||||
))
|
||||
.set_payload(Bytes::from(
|
||||
@ -713,7 +648,7 @@ async fn permissions_edit_details() {
|
||||
// Uses alpha project to delete added icon
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/project/{}/icon?ext=png",
|
||||
"/v3/project/{}/icon?ext=png",
|
||||
ctx.project_id.unwrap()
|
||||
))
|
||||
};
|
||||
@ -729,7 +664,7 @@ async fn permissions_edit_details() {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!(
|
||||
"/v2/project/{}/gallery?ext=png&featured=true",
|
||||
"/v3/project/{}/gallery?ext=png&featured=true",
|
||||
ctx.project_id.unwrap()
|
||||
))
|
||||
.set_payload(Bytes::from(
|
||||
@ -744,7 +679,7 @@ async fn permissions_edit_details() {
|
||||
.unwrap();
|
||||
// Get project, as we need the gallery image url
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/project/{alpha_project_id}"))
|
||||
.uri(&format!("/v3/project/{alpha_project_id}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -755,7 +690,7 @@ async fn permissions_edit_details() {
|
||||
// Uses alpha project to edit gallery item
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch().uri(&format!(
|
||||
"/v2/project/{}/gallery?url={gallery_url}",
|
||||
"/v3/project/{}/gallery?url={gallery_url}",
|
||||
ctx.project_id.unwrap()
|
||||
))
|
||||
};
|
||||
@ -770,7 +705,7 @@ async fn permissions_edit_details() {
|
||||
// Uses alpha project to remove gallery item
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/project/{}/gallery?url={gallery_url}",
|
||||
"/v3/project/{}/gallery?url={gallery_url}",
|
||||
ctx.project_id.unwrap()
|
||||
))
|
||||
};
|
||||
@ -794,7 +729,7 @@ async fn permissions_upload_version() {
|
||||
|
||||
// Upload version with basic-mod.jar
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::post().uri("/v2/version").set_multipart([
|
||||
test::TestRequest::post().uri("/v3/version").set_multipart([
|
||||
MultipartSegment {
|
||||
name: "data".to_string(),
|
||||
filename: None,
|
||||
@ -806,6 +741,8 @@ async fn permissions_upload_version() {
|
||||
"version_number": "1.0.0",
|
||||
"version_title": "1.0.0",
|
||||
"version_type": "release",
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"dependencies": [],
|
||||
"game_versions": ["1.20.1"],
|
||||
"loaders": ["fabric"],
|
||||
@ -834,7 +771,7 @@ async fn permissions_upload_version() {
|
||||
// Uses alpha project, as it has an existing version
|
||||
let req_gen = |_: &PermissionsTestContext| {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/version/{}/file", alpha_version_id))
|
||||
.uri(&format!("/v3/version/{}/file", alpha_version_id))
|
||||
.set_multipart([
|
||||
MultipartSegment {
|
||||
name: "data".to_string(),
|
||||
@ -868,7 +805,7 @@ async fn permissions_upload_version() {
|
||||
// Uses alpha project, as it has an existing version
|
||||
let req_gen = |_: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/version/{}", alpha_version_id))
|
||||
.uri(&format!("/v3/version/{}", alpha_version_id))
|
||||
.set_json(json!({
|
||||
"name": "Basic Mod",
|
||||
}))
|
||||
@ -884,7 +821,7 @@ async fn permissions_upload_version() {
|
||||
// Uses alpha project, as it has an existing version
|
||||
let delete_version = ProjectPermissions::DELETE_VERSION;
|
||||
let req_gen = |_: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!("/v2/version_file/{}", alpha_file_hash))
|
||||
test::TestRequest::delete().uri(&format!("/v3/version_file/{}", alpha_file_hash))
|
||||
};
|
||||
|
||||
PermissionsTest::new(&test_env)
|
||||
@ -897,7 +834,7 @@ async fn permissions_upload_version() {
|
||||
// Delete version
|
||||
// Uses alpha project, as it has an existing version
|
||||
let req_gen = |_: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!("/v2/version/{}", alpha_version_id))
|
||||
test::TestRequest::delete().uri(&format!("/v3/version/{}", alpha_version_id))
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
.with_existing_project(alpha_project_id, alpha_team_id)
|
||||
@ -921,7 +858,7 @@ async fn permissions_manage_invites() {
|
||||
// Add member
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{}/members", ctx.team_id.unwrap()))
|
||||
.uri(&format!("/v3/team/{}/members", ctx.team_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"user_id": MOD_USER_ID,
|
||||
"permissions": 0,
|
||||
@ -939,7 +876,7 @@ async fn permissions_manage_invites() {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/team/{}/members/{MOD_USER_ID}",
|
||||
"/v3/team/{}/members/{MOD_USER_ID}",
|
||||
ctx.team_id.unwrap()
|
||||
))
|
||||
.set_json(json!({
|
||||
@ -957,7 +894,7 @@ async fn permissions_manage_invites() {
|
||||
// requires manage_invites if they have not yet accepted the invite
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/team/{}/members/{MOD_USER_ID}",
|
||||
"/v3/team/{}/members/{MOD_USER_ID}",
|
||||
ctx.team_id.unwrap()
|
||||
))
|
||||
};
|
||||
@ -970,7 +907,7 @@ async fn permissions_manage_invites() {
|
||||
|
||||
// re-add member for testing
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{}/members", alpha_team_id))
|
||||
.uri(&format!("/v3/team/{}/members", alpha_team_id))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.set_json(json!({
|
||||
"user_id": MOD_USER_ID,
|
||||
@ -981,7 +918,7 @@ async fn permissions_manage_invites() {
|
||||
|
||||
// Accept invite
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{}/join", alpha_team_id))
|
||||
.uri(&format!("/v3/team/{}/join", alpha_team_id))
|
||||
.append_header(("Authorization", MOD_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -991,7 +928,7 @@ async fn permissions_manage_invites() {
|
||||
let remove_member = ProjectPermissions::REMOVE_MEMBER;
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/team/{}/members/{MOD_USER_ID}",
|
||||
"/v3/team/{}/members/{MOD_USER_ID}",
|
||||
ctx.team_id.unwrap()
|
||||
))
|
||||
};
|
||||
@ -1015,7 +952,7 @@ async fn permissions_delete_project() {
|
||||
|
||||
// Delete project
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!("/v2/project/{}", ctx.project_id.unwrap()))
|
||||
test::TestRequest::delete().uri(&format!("/v3/project/{}", ctx.project_id.unwrap()))
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
.simple_project_permissions_test(delete_project, req_gen)
|
||||
@ -1027,7 +964,7 @@ async fn permissions_delete_project() {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn project_permissions_consistency_test() {
|
||||
let test_env = TestEnvironment::build(Some(8)).await;
|
||||
let test_env = TestEnvironment::build(Some(10)).await;
|
||||
|
||||
// Test that the permissions are consistent with each other
|
||||
// For example, if we get the projectpermissions directly, from an organization's defaults, overriden, etc, they should all be correct & consistent
|
||||
@ -1036,7 +973,7 @@ async fn project_permissions_consistency_test() {
|
||||
let success_permissions = ProjectPermissions::EDIT_DETAILS;
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{}", ctx.project_id.unwrap()))
|
||||
.uri(&format!("/v3/project/{}", ctx.project_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"title": "Example title - changed.",
|
||||
}))
|
||||
@ -1053,7 +990,7 @@ async fn project_permissions_consistency_test() {
|
||||
| ProjectPermissions::VIEW_PAYOUTS;
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{}", ctx.project_id.unwrap()))
|
||||
.uri(&format!("/v3/project/{}", ctx.project_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"title": "Example title - changed.",
|
||||
}))
|
||||
|
||||
196
tests/scopes.rs
196
tests/scopes.rs
@ -1,21 +1,19 @@
|
||||
use actix_web::test::{self, TestRequest};
|
||||
use bytes::Bytes;
|
||||
use chrono::{Duration, Utc};
|
||||
use common::{database::*, environment::TestEnvironment, scopes::ScopeTest};
|
||||
use labrinth::models::pats::Scopes;
|
||||
use labrinth::util::actix::{AppendsMultipart, MultipartSegment, MultipartSegmentData};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::{database::*, environment::TestEnvironment, scopes::ScopeTest};
|
||||
|
||||
// importing common module.
|
||||
mod common;
|
||||
|
||||
// For each scope, we (using test_scope):
|
||||
// - create a PAT with a given set of scopes for a function
|
||||
// - create a PAT with all other scopes for a function
|
||||
// - test the function with the PAT with the given scopes
|
||||
// - test the function with the PAT with all other scopes
|
||||
|
||||
mod common;
|
||||
|
||||
// Test for users, emails, and payout scopes (not user auth scope or notifs)
|
||||
#[actix_rt::test]
|
||||
async fn user_scopes() {
|
||||
@ -24,7 +22,7 @@ async fn user_scopes() {
|
||||
|
||||
// User reading
|
||||
let read_user = Scopes::USER_READ;
|
||||
let req_gen = || TestRequest::get().uri("/v2/user");
|
||||
let req_gen = || TestRequest::get().uri("/v3/user");
|
||||
let (_, success) = ScopeTest::new(&test_env)
|
||||
.test(req_gen, read_user)
|
||||
.await
|
||||
@ -34,7 +32,7 @@ async fn user_scopes() {
|
||||
|
||||
// Email reading
|
||||
let read_email = Scopes::USER_READ | Scopes::USER_READ_EMAIL;
|
||||
let req_gen = || TestRequest::get().uri("/v2/user");
|
||||
let req_gen = || TestRequest::get().uri("/v3/user");
|
||||
let (_, success) = ScopeTest::new(&test_env)
|
||||
.test(req_gen, read_email)
|
||||
.await
|
||||
@ -43,7 +41,7 @@ async fn user_scopes() {
|
||||
|
||||
// Payout reading
|
||||
let read_payout = Scopes::USER_READ | Scopes::PAYOUTS_READ;
|
||||
let req_gen = || TestRequest::get().uri("/v2/user");
|
||||
let req_gen = || TestRequest::get().uri("/v3/user");
|
||||
let (_, success) = ScopeTest::new(&test_env)
|
||||
.test(req_gen, read_payout)
|
||||
.await
|
||||
@ -54,7 +52,7 @@ async fn user_scopes() {
|
||||
// We use the Admin PAT for this test, on the 'user' user
|
||||
let write_user = Scopes::USER_WRITE;
|
||||
let req_gen = || {
|
||||
TestRequest::patch().uri("/v2/user/user").set_json(json!( {
|
||||
TestRequest::patch().uri("/v3/user/user").set_json(json!( {
|
||||
// Do not include 'username', as to not change the rest of the tests
|
||||
"name": "NewName",
|
||||
"bio": "New bio",
|
||||
@ -73,7 +71,7 @@ async fn user_scopes() {
|
||||
// User deletion
|
||||
// (The failure is first, and this is the last test for this test function, we can delete it and use the same PAT for both tests)
|
||||
let delete_user = Scopes::USER_DELETE;
|
||||
let req_gen = || TestRequest::delete().uri("/v2/user/enemy");
|
||||
let req_gen = || TestRequest::delete().uri("/v3/user/enemy");
|
||||
ScopeTest::new(&test_env)
|
||||
.with_user_id(ENEMY_USER_ID_PARSED)
|
||||
.test(req_gen, delete_user)
|
||||
@ -99,7 +97,7 @@ pub async fn notifications_scopes() {
|
||||
// We will invite user 'friend' to project team, and use that as a notification
|
||||
// Get notifications
|
||||
let resp = test_env
|
||||
.v2
|
||||
.v3
|
||||
.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
@ -107,7 +105,7 @@ pub async fn notifications_scopes() {
|
||||
// Notification get
|
||||
let read_notifications = Scopes::NOTIFICATION_READ;
|
||||
let req_gen =
|
||||
|| test::TestRequest::get().uri(&format!("/v2/user/{FRIEND_USER_ID}/notifications"));
|
||||
|| test::TestRequest::get().uri(&format!("/v3/user/{FRIEND_USER_ID}/notifications"));
|
||||
let (_, success) = ScopeTest::new(&test_env)
|
||||
.with_user_id(FRIEND_USER_ID_PARSED)
|
||||
.test(req_gen, read_notifications)
|
||||
@ -117,7 +115,7 @@ pub async fn notifications_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::get().uri(&format!(
|
||||
"/v2/notifications?ids=[{uri}]",
|
||||
"/v3/notifications?ids=[{uri}]",
|
||||
uri = urlencoding::encode(&format!("\"{notification_id}\""))
|
||||
))
|
||||
};
|
||||
@ -127,7 +125,7 @@ pub async fn notifications_scopes() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/notification/{notification_id}"));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/notification/{notification_id}"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_user_id(FRIEND_USER_ID_PARSED)
|
||||
.test(req_gen, read_notifications)
|
||||
@ -138,7 +136,7 @@ pub async fn notifications_scopes() {
|
||||
let write_notifications = Scopes::NOTIFICATION_WRITE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch().uri(&format!(
|
||||
"/v2/notifications?ids=[{uri}]",
|
||||
"/v3/notifications?ids=[{uri}]",
|
||||
uri = urlencoding::encode(&format!("\"{notification_id}\""))
|
||||
))
|
||||
};
|
||||
@ -148,7 +146,7 @@ pub async fn notifications_scopes() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req_gen = || test::TestRequest::patch().uri(&format!("/v2/notification/{notification_id}"));
|
||||
let req_gen = || test::TestRequest::patch().uri(&format!("/v3/notification/{notification_id}"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_user_id(FRIEND_USER_ID_PARSED)
|
||||
.test(req_gen, write_notifications)
|
||||
@ -157,7 +155,7 @@ pub async fn notifications_scopes() {
|
||||
|
||||
// Notification delete
|
||||
let req_gen =
|
||||
|| test::TestRequest::delete().uri(&format!("/v2/notification/{notification_id}"));
|
||||
|| test::TestRequest::delete().uri(&format!("/v3/notification/{notification_id}"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_user_id(FRIEND_USER_ID_PARSED)
|
||||
.test(req_gen, write_notifications)
|
||||
@ -167,12 +165,12 @@ pub async fn notifications_scopes() {
|
||||
// Mass notification delete
|
||||
// We invite mod, get the notification ID, and do mass delete using that
|
||||
let resp = test_env
|
||||
.v2
|
||||
.v3
|
||||
.add_user_to_team(alpha_team_id, MOD_USER_ID, None, None, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
let read_notifications = Scopes::NOTIFICATION_READ;
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/user/{MOD_USER_ID}/notifications"));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/user/{MOD_USER_ID}/notifications"));
|
||||
let (_, success) = ScopeTest::new(&test_env)
|
||||
.with_user_id(MOD_USER_ID_PARSED)
|
||||
.test(req_gen, read_notifications)
|
||||
@ -182,7 +180,7 @@ pub async fn notifications_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/notifications?ids=[{uri}]",
|
||||
"/v3/notifications?ids=[{uri}]",
|
||||
uri = urlencoding::encode(&format!("\"{notification_id}\""))
|
||||
))
|
||||
};
|
||||
@ -209,14 +207,14 @@ pub async fn project_version_create_scopes() {
|
||||
"slug": "demo",
|
||||
"description": "Example description.",
|
||||
"body": "Example body.",
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"initial_versions": [{
|
||||
"file_parts": ["basic-mod.jar"],
|
||||
"version_number": "1.2.3",
|
||||
"version_title": "start",
|
||||
"dependencies": [],
|
||||
"game_versions": ["1.20.1"] ,
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"release_channel": "release",
|
||||
"loaders": ["fabric"],
|
||||
"featured": true
|
||||
@ -240,7 +238,7 @@ pub async fn project_version_create_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.uri("/v3/project")
|
||||
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
|
||||
};
|
||||
let (_, success) = ScopeTest::new(&test_env)
|
||||
@ -259,6 +257,8 @@ pub async fn project_version_create_scopes() {
|
||||
"version_title": "start",
|
||||
"dependencies": [],
|
||||
"game_versions": ["1.20.1"] ,
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"release_channel": "release",
|
||||
"loaders": ["fabric"],
|
||||
"featured": true
|
||||
@ -281,7 +281,7 @@ pub async fn project_version_create_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri("/v2/version")
|
||||
.uri("/v3/version")
|
||||
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
|
||||
};
|
||||
ScopeTest::new(&test_env)
|
||||
@ -329,7 +329,7 @@ pub async fn project_version_reads_scopes() {
|
||||
// Project reading
|
||||
// Uses 404 as the expected failure code (or 200 and an empty list for mass reads)
|
||||
let read_project = Scopes::PROJECT_READ;
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/project/{beta_project_id}"));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/project/{beta_project_id}"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_failure_code(404)
|
||||
.test(req_gen, read_project)
|
||||
@ -337,7 +337,7 @@ pub async fn project_version_reads_scopes() {
|
||||
.unwrap();
|
||||
|
||||
let req_gen =
|
||||
|| test::TestRequest::get().uri(&format!("/v2/project/{beta_project_id}/dependencies"));
|
||||
|| test::TestRequest::get().uri(&format!("/v3/project/{beta_project_id}/dependencies"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_failure_code(404)
|
||||
.test(req_gen, read_project)
|
||||
@ -346,7 +346,7 @@ pub async fn project_version_reads_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::get().uri(&format!(
|
||||
"/v2/projects?ids=[{uri}]",
|
||||
"/v3/projects?ids=[{uri}]",
|
||||
uri = urlencoding::encode(&format!("\"{beta_project_id}\""))
|
||||
))
|
||||
};
|
||||
@ -360,7 +360,7 @@ pub async fn project_version_reads_scopes() {
|
||||
|
||||
// Team project reading
|
||||
let req_gen =
|
||||
|| test::TestRequest::get().uri(&format!("/v2/project/{beta_project_id}/members"));
|
||||
|| test::TestRequest::get().uri(&format!("/v3/project/{beta_project_id}/members"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_failure_code(404)
|
||||
.test(req_gen, read_project)
|
||||
@ -370,7 +370,7 @@ pub async fn project_version_reads_scopes() {
|
||||
// Get team members
|
||||
// In this case, as these are public endpoints, logging in only is relevant to showing permissions
|
||||
// So for our test project (with 1 user, 'user') we will check the permissions before and after having the scope.
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/team/{alpha_team_id}/members"));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/team/{alpha_team_id}/members"));
|
||||
let (failure, success) = ScopeTest::new(&test_env)
|
||||
.with_failure_code(200)
|
||||
.test(req_gen, read_project)
|
||||
@ -381,7 +381,7 @@ pub async fn project_version_reads_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::get().uri(&format!(
|
||||
"/v2/teams?ids=[{uri}]",
|
||||
"/v3/teams?ids=[{uri}]",
|
||||
uri = urlencoding::encode(&format!("\"{alpha_team_id}\""))
|
||||
))
|
||||
};
|
||||
@ -395,7 +395,7 @@ pub async fn project_version_reads_scopes() {
|
||||
|
||||
// User project reading
|
||||
// Test user has two projects, one public and one private
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/user/{USER_USER_ID}/projects"));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/user/{USER_USER_ID}/projects"));
|
||||
let (failure, success) = ScopeTest::new(&test_env)
|
||||
.with_failure_code(200)
|
||||
.test(req_gen, read_project)
|
||||
@ -428,7 +428,7 @@ pub async fn project_version_reads_scopes() {
|
||||
// First, set version to hidden (which is when the scope is required to read it)
|
||||
let read_version = Scopes::VERSION_READ;
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/version/{beta_version_id}"))
|
||||
.uri(&format!("/v3/version/{beta_version_id}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"status": "draft"
|
||||
@ -437,7 +437,7 @@ pub async fn project_version_reads_scopes() {
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/version_file/{beta_file_hash}"));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/version_file/{beta_file_hash}"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_failure_code(404)
|
||||
.test(req_gen, read_version)
|
||||
@ -445,7 +445,7 @@ pub async fn project_version_reads_scopes() {
|
||||
.unwrap();
|
||||
|
||||
let req_gen =
|
||||
|| test::TestRequest::get().uri(&format!("/v2/version_file/{beta_file_hash}/download"));
|
||||
|| test::TestRequest::get().uri(&format!("/v3/version_file/{beta_file_hash}/download"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_failure_code(404)
|
||||
.test(req_gen, read_version)
|
||||
@ -456,7 +456,7 @@ pub async fn project_version_reads_scopes() {
|
||||
// TODO: this scope doesn't actually affect anything, because the Project::get_id contained within disallows hidden versions, which is the point of this scope
|
||||
// let req_gen = || {
|
||||
// test::TestRequest::post()
|
||||
// .uri(&format!("/v2/version_file/{beta_file_hash}/update"))
|
||||
// .uri(&format!("/v3/version_file/{beta_file_hash}/update"))
|
||||
// .set_json(json!({}))
|
||||
// };
|
||||
// ScopeTest::new(&test_env).with_failure_code(404).test(req_gen, read_version).await.unwrap();
|
||||
@ -464,7 +464,7 @@ pub async fn project_version_reads_scopes() {
|
||||
// TODO: Should this be /POST? Looks like /GET
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri("/v2/version_files")
|
||||
.uri("/v3/version_files")
|
||||
.set_json(json!({
|
||||
"hashes": [beta_file_hash]
|
||||
}))
|
||||
@ -483,7 +483,7 @@ pub async fn project_version_reads_scopes() {
|
||||
|
||||
// let req_gen = || {
|
||||
// test::TestRequest::post()
|
||||
// .uri(&format!("/v2/version_files/update_individual"))
|
||||
// .uri(&format!("/v3/version_files/update_individual"))
|
||||
// .set_json(json!({
|
||||
// "hashes": [{
|
||||
// "hash": beta_file_hash,
|
||||
@ -498,7 +498,7 @@ pub async fn project_version_reads_scopes() {
|
||||
// TODO: this scope doesn't actually affect anything, because the Project::get_id contained within disallows hidden versions, which is the point of this scope
|
||||
// let req_gen = || {
|
||||
// test::TestRequest::post()
|
||||
// .uri(&format!("/v2/version_files/update"))
|
||||
// .uri(&format!("/v3/version_files/update"))
|
||||
// .set_json(json!({
|
||||
// "hashes": [beta_file_hash]
|
||||
// }))
|
||||
@ -510,7 +510,7 @@ pub async fn project_version_reads_scopes() {
|
||||
// Both project and version reading
|
||||
let read_project_and_version = Scopes::PROJECT_READ | Scopes::VERSION_READ;
|
||||
let req_gen =
|
||||
|| test::TestRequest::get().uri(&format!("/v2/project/{beta_project_id}/version"));
|
||||
|| test::TestRequest::get().uri(&format!("/v3/project/{beta_project_id}/version"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_failure_code(404)
|
||||
.test(req_gen, read_project_and_version)
|
||||
@ -520,7 +520,7 @@ pub async fn project_version_reads_scopes() {
|
||||
// TODO: fails for the same reason as above
|
||||
// let req_gen = || {
|
||||
// test::TestRequest::get()
|
||||
// .uri(&format!("/v2/project/{beta_project_id}/version/{beta_version_id}"))
|
||||
// .uri(&format!("/v3/project/{beta_project_id}/version/{beta_version_id}"))
|
||||
// };
|
||||
// ScopeTest::new(&test_env).with_failure_code(404).test(req_gen, read_project_and_version).await.unwrap();
|
||||
|
||||
@ -552,7 +552,7 @@ pub async fn project_write_scopes() {
|
||||
let write_project = Scopes::PROJECT_WRITE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{beta_project_id}"))
|
||||
.uri(&format!("/v3/project/{beta_project_id}"))
|
||||
.set_json(json!(
|
||||
{
|
||||
"title": "test_project_version_write_scopes Title",
|
||||
@ -567,7 +567,7 @@ pub async fn project_write_scopes() {
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/projects?ids=[{uri}]",
|
||||
"/v3/projects?ids=[{uri}]",
|
||||
uri = urlencoding::encode(&format!("\"{beta_project_id}\""))
|
||||
))
|
||||
.set_json(json!(
|
||||
@ -583,7 +583,7 @@ pub async fn project_write_scopes() {
|
||||
|
||||
// Approve beta as private so we can schedule it
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{beta_project_id}"))
|
||||
.uri(&format!("/v3/project/{beta_project_id}"))
|
||||
.append_header(("Authorization", MOD_USER_PAT))
|
||||
.set_json(json!({
|
||||
"status": "private"
|
||||
@ -594,7 +594,7 @@ pub async fn project_write_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/project/{beta_project_id}/schedule")) // beta_project_id is an unpublished can schedule it
|
||||
.uri(&format!("/v3/project/{beta_project_id}/schedule")) // beta_project_id is an unpublished can schedule it
|
||||
.set_json(json!(
|
||||
{
|
||||
"requested_status": "private",
|
||||
@ -610,7 +610,7 @@ pub async fn project_write_scopes() {
|
||||
// Icons and gallery images
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{beta_project_id}/icon?ext=png"))
|
||||
.uri(&format!("/v3/project/{beta_project_id}/icon?ext=png"))
|
||||
.set_payload(Bytes::from(
|
||||
include_bytes!("../tests/files/200x200.png") as &[u8]
|
||||
))
|
||||
@ -621,7 +621,7 @@ pub async fn project_write_scopes() {
|
||||
.unwrap();
|
||||
|
||||
let req_gen =
|
||||
|| test::TestRequest::delete().uri(&format!("/v2/project/{beta_project_id}/icon"));
|
||||
|| test::TestRequest::delete().uri(&format!("/v3/project/{beta_project_id}/icon"));
|
||||
ScopeTest::new(&test_env)
|
||||
.test(req_gen, write_project)
|
||||
.await
|
||||
@ -630,7 +630,7 @@ pub async fn project_write_scopes() {
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!(
|
||||
"/v2/project/{beta_project_id}/gallery?ext=png&featured=true"
|
||||
"/v3/project/{beta_project_id}/gallery?ext=png&featured=true"
|
||||
))
|
||||
.set_payload(Bytes::from(
|
||||
include_bytes!("../tests/files/200x200.png") as &[u8]
|
||||
@ -643,7 +643,7 @@ pub async fn project_write_scopes() {
|
||||
|
||||
// Get project, as we need the gallery image url
|
||||
let req_gen = test::TestRequest::get()
|
||||
.uri(&format!("/v2/project/{beta_project_id}"))
|
||||
.uri(&format!("/v3/project/{beta_project_id}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req_gen).await;
|
||||
@ -652,7 +652,7 @@ pub async fn project_write_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch().uri(&format!(
|
||||
"/v2/project/{beta_project_id}/gallery?url={gallery_url}"
|
||||
"/v3/project/{beta_project_id}/gallery?url={gallery_url}"
|
||||
))
|
||||
};
|
||||
ScopeTest::new(&test_env)
|
||||
@ -662,7 +662,7 @@ pub async fn project_write_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/project/{beta_project_id}/gallery?url={gallery_url}"
|
||||
"/v3/project/{beta_project_id}/gallery?url={gallery_url}"
|
||||
))
|
||||
};
|
||||
ScopeTest::new(&test_env)
|
||||
@ -673,7 +673,7 @@ pub async fn project_write_scopes() {
|
||||
// Team scopes - add user 'friend'
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members"))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID
|
||||
}))
|
||||
@ -684,7 +684,7 @@ pub async fn project_write_scopes() {
|
||||
.unwrap();
|
||||
|
||||
// Accept team invite as 'friend'
|
||||
let req_gen = || test::TestRequest::post().uri(&format!("/v2/team/{alpha_team_id}/join"));
|
||||
let req_gen = || test::TestRequest::post().uri(&format!("/v3/team/{alpha_team_id}/join"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_user_id(FRIEND_USER_ID_PARSED)
|
||||
.test(req_gen, write_project)
|
||||
@ -695,7 +695,7 @@ pub async fn project_write_scopes() {
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
"/v3/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
))
|
||||
.set_json(json!({
|
||||
"permissions": 1
|
||||
@ -709,7 +709,7 @@ pub async fn project_write_scopes() {
|
||||
// Transfer ownership to 'friend'
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/owner"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/owner"))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID
|
||||
}))
|
||||
@ -721,7 +721,7 @@ pub async fn project_write_scopes() {
|
||||
|
||||
// Now as 'friend', delete 'user'
|
||||
let req_gen = || {
|
||||
test::TestRequest::delete().uri(&format!("/v2/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
test::TestRequest::delete().uri(&format!("/v3/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
};
|
||||
ScopeTest::new(&test_env)
|
||||
.with_user_id(FRIEND_USER_ID_PARSED)
|
||||
@ -736,7 +736,7 @@ pub async fn project_write_scopes() {
|
||||
// let delete_version = Scopes::PROJECT_DELETE;
|
||||
// let req_gen = || {
|
||||
// test::TestRequest::delete()
|
||||
// .uri(&format!("/v2/project/{beta_project_id}"))
|
||||
// .uri(&format!("/v3/project/{beta_project_id}"))
|
||||
// };
|
||||
// ScopeTest::new(&test_env).test(req_gen, delete_version).await.unwrap();
|
||||
|
||||
@ -775,7 +775,7 @@ pub async fn version_write_scopes() {
|
||||
|
||||
// Approve beta version as private so we can schedule it
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/version/{beta_version_id}"))
|
||||
.uri(&format!("/v3/version/{beta_version_id}"))
|
||||
.append_header(("Authorization", MOD_USER_PAT))
|
||||
.set_json(json!({
|
||||
"status": "unlisted"
|
||||
@ -787,7 +787,7 @@ pub async fn version_write_scopes() {
|
||||
// Schedule version
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/version/{beta_version_id}/schedule")) // beta_version_id is an *approved* version, so we can schedule it
|
||||
.uri(&format!("/v3/version/{beta_version_id}/schedule")) // beta_version_id is an *approved* version, so we can schedule it
|
||||
.set_json(json!(
|
||||
{
|
||||
"requested_status": "archived",
|
||||
@ -803,10 +803,10 @@ pub async fn version_write_scopes() {
|
||||
// Patch version
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/version/{alpha_version_id}"))
|
||||
.uri(&format!("/v3/version/{alpha_version_id}"))
|
||||
.set_json(json!(
|
||||
{
|
||||
"version_title": "test_version_write_scopes Title",
|
||||
"name": "test_version_write_scopes Title",
|
||||
}
|
||||
))
|
||||
};
|
||||
@ -846,7 +846,7 @@ pub async fn version_write_scopes() {
|
||||
// Upload version file
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/version/{alpha_version_id}/file"))
|
||||
.uri(&format!("/v3/version/{alpha_version_id}/file"))
|
||||
.set_multipart(vec![json_segment.clone(), content_segment.clone()])
|
||||
};
|
||||
ScopeTest::new(&test_env)
|
||||
@ -857,7 +857,7 @@ pub async fn version_write_scopes() {
|
||||
// Delete version file
|
||||
// TODO: Should this scope be VERSION_DELETE?
|
||||
let req_gen = || {
|
||||
test::TestRequest::delete().uri(&format!("/v2/version_file/{alpha_file_hash}"))
|
||||
test::TestRequest::delete().uri(&format!("/v3/version_file/{alpha_file_hash}"))
|
||||
// Delete from alpha_version_id, as we uploaded to alpha_version_id and it needs another file
|
||||
};
|
||||
ScopeTest::new(&test_env)
|
||||
@ -867,7 +867,7 @@ pub async fn version_write_scopes() {
|
||||
|
||||
// Delete version
|
||||
let delete_version = Scopes::VERSION_DELETE;
|
||||
let req_gen = || test::TestRequest::delete().uri(&format!("/v2/version/{alpha_version_id}"));
|
||||
let req_gen = || test::TestRequest::delete().uri(&format!("/v3/version/{alpha_version_id}"));
|
||||
ScopeTest::new(&test_env)
|
||||
.test(req_gen, delete_version)
|
||||
.await
|
||||
@ -893,7 +893,7 @@ pub async fn report_scopes() {
|
||||
// Create report
|
||||
let report_create = Scopes::REPORT_CREATE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::post().uri("/v2/report").set_json(json!({
|
||||
test::TestRequest::post().uri("/v3/report").set_json(json!({
|
||||
"report_type": "copyright",
|
||||
"item_id": beta_project_id,
|
||||
"item_type": "project",
|
||||
@ -907,14 +907,14 @@ pub async fn report_scopes() {
|
||||
|
||||
// Get reports
|
||||
let report_read = Scopes::REPORT_READ;
|
||||
let req_gen = || test::TestRequest::get().uri("/v2/report");
|
||||
let req_gen = || test::TestRequest::get().uri("/v3/report");
|
||||
let (_, success) = ScopeTest::new(&test_env)
|
||||
.test(req_gen, report_read)
|
||||
.await
|
||||
.unwrap();
|
||||
let report_id = success[0]["id"].as_str().unwrap();
|
||||
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/report/{}", report_id));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/report/{}", report_id));
|
||||
ScopeTest::new(&test_env)
|
||||
.test(req_gen, report_read)
|
||||
.await
|
||||
@ -922,7 +922,7 @@ pub async fn report_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::get().uri(&format!(
|
||||
"/v2/reports?ids=[{}]",
|
||||
"/v3/reports?ids=[{}]",
|
||||
urlencoding::encode(&format!("\"{}\"", report_id))
|
||||
))
|
||||
};
|
||||
@ -935,7 +935,7 @@ pub async fn report_scopes() {
|
||||
let report_edit = Scopes::REPORT_WRITE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/report/{}", report_id))
|
||||
.uri(&format!("/v3/report/{}", report_id))
|
||||
.set_json(json!({
|
||||
"body": "This is a reupload of my mod, G8!",
|
||||
}))
|
||||
@ -948,7 +948,7 @@ pub async fn report_scopes() {
|
||||
// Delete report
|
||||
// We use a moderator PAT here, as only moderators can delete reports
|
||||
let report_delete = Scopes::REPORT_DELETE;
|
||||
let req_gen = || test::TestRequest::delete().uri(&format!("/v2/report/{}", report_id));
|
||||
let req_gen = || test::TestRequest::delete().uri(&format!("/v3/report/{}", report_id));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_user_id(MOD_USER_ID_PARSED)
|
||||
.test(req_gen, report_delete)
|
||||
@ -981,7 +981,7 @@ pub async fn thread_scopes() {
|
||||
|
||||
// Thread read
|
||||
let thread_read = Scopes::THREAD_READ;
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/thread/{alpha_thread_id}"));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/thread/{alpha_thread_id}"));
|
||||
ScopeTest::new(&test_env)
|
||||
.test(req_gen, thread_read)
|
||||
.await
|
||||
@ -989,7 +989,7 @@ pub async fn thread_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::get().uri(&format!(
|
||||
"/v2/threads?ids=[{}]",
|
||||
"/v3/threads?ids=[{}]",
|
||||
urlencoding::encode(&format!("\"{}\"", "U"))
|
||||
))
|
||||
};
|
||||
@ -1002,7 +1002,7 @@ pub async fn thread_scopes() {
|
||||
let thread_write = Scopes::THREAD_WRITE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/thread/{beta_thread_id}"))
|
||||
.uri(&format!("/v3/thread/{beta_thread_id}"))
|
||||
.set_json(json!({
|
||||
"body": {
|
||||
"type": "text",
|
||||
@ -1018,17 +1018,17 @@ pub async fn thread_scopes() {
|
||||
|
||||
// Check moderation inbox
|
||||
// Uses moderator PAT, as only moderators can see the moderation inbox
|
||||
let req_gen = || test::TestRequest::get().uri("/v2/thread/inbox");
|
||||
let req_gen = || test::TestRequest::get().uri("/v3/thread/inbox");
|
||||
let (_, success) = ScopeTest::new(&test_env)
|
||||
.with_user_id(MOD_USER_ID_PARSED)
|
||||
.test(req_gen, thread_read)
|
||||
.await
|
||||
.unwrap();
|
||||
let thread_id = success[0]["id"].as_str().unwrap();
|
||||
let thread_id: &str = success[0]["id"].as_str().unwrap();
|
||||
|
||||
// Moderator 'read' thread
|
||||
// Uses moderator PAT, as only moderators can see the moderation inbox
|
||||
let req_gen = || test::TestRequest::post().uri(&format!("/v2/thread/{thread_id}/read"));
|
||||
let req_gen = || test::TestRequest::post().uri(&format!("/v3/thread/{thread_id}/read"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_user_id(MOD_USER_ID_PARSED)
|
||||
.test(req_gen, thread_read)
|
||||
@ -1038,14 +1038,14 @@ pub async fn thread_scopes() {
|
||||
// Delete that message
|
||||
// First, get message id
|
||||
let req_gen = test::TestRequest::get()
|
||||
.uri(&format!("/v2/thread/{thread_id}"))
|
||||
.uri(&format!("/v3/thread/{thread_id}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req_gen).await;
|
||||
let success: serde_json::Value = test::read_body_json(resp).await;
|
||||
let thread_message_id = success["messages"][0]["id"].as_str().unwrap();
|
||||
|
||||
let req_gen = || test::TestRequest::delete().uri(&format!("/v2/message/{thread_message_id}"));
|
||||
let req_gen = || test::TestRequest::delete().uri(&format!("/v3/message/{thread_message_id}"));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_user_id(MOD_USER_ID_PARSED)
|
||||
.test(req_gen, thread_write)
|
||||
@ -1064,7 +1064,7 @@ pub async fn pat_scopes() {
|
||||
// Pat create
|
||||
let pat_create = Scopes::PAT_CREATE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::post().uri("/v2/pat").set_json(json!({
|
||||
test::TestRequest::post().uri("/v3/pat").set_json(json!({
|
||||
"scopes": 1,
|
||||
"name": "test_pat_scopes Name",
|
||||
"expires": Utc::now() + Duration::days(1),
|
||||
@ -1080,7 +1080,7 @@ pub async fn pat_scopes() {
|
||||
let pat_write = Scopes::PAT_WRITE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/pat/{pat_id}"))
|
||||
.uri(&format!("/v3/pat/{pat_id}"))
|
||||
.set_json(json!({}))
|
||||
};
|
||||
ScopeTest::new(&test_env)
|
||||
@ -1090,7 +1090,7 @@ pub async fn pat_scopes() {
|
||||
|
||||
// Pat read
|
||||
let pat_read = Scopes::PAT_READ;
|
||||
let req_gen = || test::TestRequest::get().uri("/v2/pat");
|
||||
let req_gen = || test::TestRequest::get().uri("/v3/pat");
|
||||
ScopeTest::new(&test_env)
|
||||
.test(req_gen, pat_read)
|
||||
.await
|
||||
@ -1098,7 +1098,7 @@ pub async fn pat_scopes() {
|
||||
|
||||
// Pat delete
|
||||
let pat_delete = Scopes::PAT_DELETE;
|
||||
let req_gen = || test::TestRequest::delete().uri(&format!("/v2/pat/{pat_id}"));
|
||||
let req_gen = || test::TestRequest::delete().uri(&format!("/v3/pat/{pat_id}"));
|
||||
ScopeTest::new(&test_env)
|
||||
.test(req_gen, pat_delete)
|
||||
.await
|
||||
@ -1125,7 +1125,7 @@ pub async fn collections_scopes() {
|
||||
let collection_create = Scopes::COLLECTION_CREATE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri("/v2/collection")
|
||||
.uri("/v3/collection")
|
||||
.set_json(json!({
|
||||
"title": "Test Collection",
|
||||
"description": "Test Collection Description",
|
||||
@ -1143,7 +1143,7 @@ pub async fn collections_scopes() {
|
||||
let collection_write = Scopes::COLLECTION_WRITE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/collection/{collection_id}"))
|
||||
.uri(&format!("/v3/collection/{collection_id}"))
|
||||
.set_json(json!({
|
||||
"title": "Test Collection patch",
|
||||
"status": "private",
|
||||
@ -1156,7 +1156,7 @@ pub async fn collections_scopes() {
|
||||
|
||||
// Read collection
|
||||
let collection_read = Scopes::COLLECTION_READ;
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/collection/{}", collection_id));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/collection/{}", collection_id));
|
||||
ScopeTest::new(&test_env)
|
||||
.with_failure_code(404)
|
||||
.test(req_gen, collection_read)
|
||||
@ -1165,7 +1165,7 @@ pub async fn collections_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::get().uri(&format!(
|
||||
"/v2/collections?ids=[{}]",
|
||||
"/v3/collections?ids=[{}]",
|
||||
urlencoding::encode(&format!("\"{}\"", collection_id))
|
||||
))
|
||||
};
|
||||
@ -1177,7 +1177,7 @@ pub async fn collections_scopes() {
|
||||
assert_eq!(failure.as_array().unwrap().len(), 0);
|
||||
assert_eq!(success.as_array().unwrap().len(), 1);
|
||||
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/user/{USER_USER_ID}/collections"));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/user/{USER_USER_ID}/collections"));
|
||||
let (failure, success) = ScopeTest::new(&test_env)
|
||||
.with_failure_code(200)
|
||||
.test(req_gen, collection_read)
|
||||
@ -1188,7 +1188,7 @@ pub async fn collections_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/collection/{collection_id}/icon?ext=png"))
|
||||
.uri(&format!("/v3/collection/{collection_id}/icon?ext=png"))
|
||||
.set_payload(Bytes::from(
|
||||
include_bytes!("../tests/files/200x200.png") as &[u8]
|
||||
))
|
||||
@ -1199,7 +1199,7 @@ pub async fn collections_scopes() {
|
||||
.unwrap();
|
||||
|
||||
let req_gen =
|
||||
|| test::TestRequest::delete().uri(&format!("/v2/collection/{collection_id}/icon"));
|
||||
|| test::TestRequest::delete().uri(&format!("/v3/collection/{collection_id}/icon"));
|
||||
ScopeTest::new(&test_env)
|
||||
.test(req_gen, collection_write)
|
||||
.await
|
||||
@ -1226,7 +1226,7 @@ pub async fn organization_scopes() {
|
||||
let organization_create = Scopes::ORGANIZATION_CREATE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri("/v2/organization")
|
||||
.uri("/v3/organization")
|
||||
.set_json(json!({
|
||||
"title": "TestOrg",
|
||||
"description": "TestOrg Description",
|
||||
@ -1242,7 +1242,7 @@ pub async fn organization_scopes() {
|
||||
let organization_edit = Scopes::ORGANIZATION_WRITE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/organization/{organization_id}"))
|
||||
.uri(&format!("/v3/organization/{organization_id}"))
|
||||
.set_json(json!({
|
||||
"description": "TestOrg Patch Description",
|
||||
}))
|
||||
@ -1254,7 +1254,7 @@ pub async fn organization_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/organization/{organization_id}/icon?ext=png"))
|
||||
.uri(&format!("/v3/organization/{organization_id}/icon?ext=png"))
|
||||
.set_payload(Bytes::from(
|
||||
include_bytes!("../tests/files/200x200.png") as &[u8]
|
||||
))
|
||||
@ -1265,7 +1265,7 @@ pub async fn organization_scopes() {
|
||||
.unwrap();
|
||||
|
||||
let req_gen =
|
||||
|| test::TestRequest::delete().uri(&format!("/v2/organization/{organization_id}/icon"));
|
||||
|| test::TestRequest::delete().uri(&format!("/v3/organization/{organization_id}/icon"));
|
||||
ScopeTest::new(&test_env)
|
||||
.test(req_gen, organization_edit)
|
||||
.await
|
||||
@ -1275,7 +1275,7 @@ pub async fn organization_scopes() {
|
||||
let organization_project_edit = Scopes::PROJECT_WRITE | Scopes::ORGANIZATION_WRITE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/organization/{organization_id}/projects"))
|
||||
.uri(&format!("/v3/organization/{organization_id}/projects"))
|
||||
.set_json(json!({
|
||||
"project_id": beta_project_id
|
||||
}))
|
||||
@ -1288,7 +1288,7 @@ pub async fn organization_scopes() {
|
||||
|
||||
// Organization reads
|
||||
let organization_read = Scopes::ORGANIZATION_READ;
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v2/organization/{organization_id}"));
|
||||
let req_gen = || test::TestRequest::get().uri(&format!("/v3/organization/{organization_id}"));
|
||||
let (failure, success) = ScopeTest::new(&test_env)
|
||||
.with_failure_code(200)
|
||||
.test(req_gen, organization_read)
|
||||
@ -1299,7 +1299,7 @@ pub async fn organization_scopes() {
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::get().uri(&format!(
|
||||
"/v2/organizations?ids=[{}]",
|
||||
"/v3/organizations?ids=[{}]",
|
||||
urlencoding::encode(&format!("\"{}\"", organization_id))
|
||||
))
|
||||
};
|
||||
@ -1314,7 +1314,7 @@ pub async fn organization_scopes() {
|
||||
|
||||
let organization_project_read = Scopes::PROJECT_READ | Scopes::ORGANIZATION_READ;
|
||||
let req_gen =
|
||||
|| test::TestRequest::get().uri(&format!("/v2/organization/{organization_id}/projects"));
|
||||
|| test::TestRequest::get().uri(&format!("/v3/organization/{organization_id}/projects"));
|
||||
let (failure, success) = ScopeTest::new(&test_env)
|
||||
.with_failure_code(200)
|
||||
.with_failure_scopes(Scopes::all() ^ Scopes::ORGANIZATION_READ)
|
||||
@ -1327,7 +1327,7 @@ pub async fn organization_scopes() {
|
||||
// remove project (now that we've checked)
|
||||
let req_gen = || {
|
||||
test::TestRequest::delete().uri(&format!(
|
||||
"/v2/organization/{organization_id}/projects/{beta_project_id}"
|
||||
"/v3/organization/{organization_id}/projects/{beta_project_id}"
|
||||
))
|
||||
};
|
||||
ScopeTest::new(&test_env)
|
||||
@ -1339,7 +1339,7 @@ pub async fn organization_scopes() {
|
||||
// Delete organization
|
||||
let organization_delete = Scopes::ORGANIZATION_DELETE;
|
||||
let req_gen =
|
||||
|| test::TestRequest::delete().uri(&format!("/v2/organization/{organization_id}"));
|
||||
|| test::TestRequest::delete().uri(&format!("/v3/organization/{organization_id}"));
|
||||
ScopeTest::new(&test_env)
|
||||
.test(req_gen, organization_delete)
|
||||
.await
|
||||
|
||||
@ -1,23 +1,27 @@
|
||||
use crate::common::database::*;
|
||||
use crate::common::dummy_data::DUMMY_CATEGORIES;
|
||||
use crate::common::environment::TestEnvironment;
|
||||
use crate::common::request_data::{get_public_version_creation_data, ProjectCreationRequestData};
|
||||
use common::database::*;
|
||||
use common::dummy_data::TestFile;
|
||||
use common::request_data;
|
||||
use common::dummy_data::DUMMY_CATEGORIES;
|
||||
use common::environment::TestEnvironment;
|
||||
use futures::stream::StreamExt;
|
||||
use labrinth::models::ids::base62_impl::parse_base62;
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
// importing common module.
|
||||
use crate::common::api_v3::request_data::{
|
||||
self, get_public_version_creation_data, ProjectCreationRequestData,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
// TODO: Revisit this with the new modify_json in the version maker
|
||||
// That change here should be able to simplify it vastly
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_projects() {
|
||||
// Test setup and dummy data
|
||||
let test_env = TestEnvironment::build(Some(8)).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
let test_name = test_env.db.database_name.clone();
|
||||
|
||||
// Add dummy projects of various categories for searchability
|
||||
@ -38,7 +42,6 @@ async fn search_projects() {
|
||||
let mut basic_project_json =
|
||||
request_data::get_public_project_creation_data_json(&slug, Some(&jar));
|
||||
modify_json(&mut basic_project_json);
|
||||
|
||||
let basic_project_multipart =
|
||||
request_data::get_public_creation_data_multipart(&basic_project_json, Some(&jar));
|
||||
// Add a project- simple, should work.
|
||||
@ -72,7 +75,7 @@ async fn search_projects() {
|
||||
let id = 0;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[4..6]);
|
||||
json["server_side"] = json!("required");
|
||||
json["initial_versions"][0]["server_side"] = json!("required");
|
||||
json["license_id"] = json!("LGPL-3.0-or-later");
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
@ -86,7 +89,7 @@ async fn search_projects() {
|
||||
let id = 1;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[0..2]);
|
||||
json["client_side"] = json!("optional");
|
||||
json["initial_versions"][0]["client_side"] = json!("optional");
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
id,
|
||||
@ -99,7 +102,7 @@ async fn search_projects() {
|
||||
let id = 2;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[0..2]);
|
||||
json["server_side"] = json!("required");
|
||||
json["initial_versions"][0]["server_side"] = json!("required");
|
||||
json["title"] = json!("Mysterious Project");
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
@ -113,7 +116,7 @@ async fn search_projects() {
|
||||
let id = 3;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[0..3]);
|
||||
json["server_side"] = json!("required");
|
||||
json["initial_versions"][0]["server_side"] = json!("required");
|
||||
json["initial_versions"][0]["game_versions"] = json!(["1.20.4"]);
|
||||
json["title"] = json!("Mysterious Project");
|
||||
json["license_id"] = json!("LicenseRef-All-Rights-Reserved"); // closed source
|
||||
@ -129,7 +132,7 @@ async fn search_projects() {
|
||||
let id = 4;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[0..3]);
|
||||
json["client_side"] = json!("optional");
|
||||
json["initial_versions"][0]["client_side"] = json!("optional");
|
||||
json["initial_versions"][0]["game_versions"] = json!(["1.20.5"]);
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
@ -143,7 +146,7 @@ async fn search_projects() {
|
||||
let id = 5;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[5..6]);
|
||||
json["client_side"] = json!("optional");
|
||||
json["initial_versions"][0]["client_side"] = json!("optional");
|
||||
json["initial_versions"][0]["game_versions"] = json!(["1.20.5"]);
|
||||
json["license_id"] = json!("LGPL-3.0-or-later");
|
||||
};
|
||||
@ -158,8 +161,8 @@ async fn search_projects() {
|
||||
let id = 6;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[5..6]);
|
||||
json["client_side"] = json!("optional");
|
||||
json["server_side"] = json!("required");
|
||||
json["initial_versions"][0]["client_side"] = json!("optional");
|
||||
json["initial_versions"][0]["server_side"] = json!("required");
|
||||
json["license_id"] = json!("LGPL-3.0-or-later");
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
@ -175,8 +178,8 @@ async fn search_projects() {
|
||||
let id = 7;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[5..6]);
|
||||
json["client_side"] = json!("optional");
|
||||
json["server_side"] = json!("required");
|
||||
json["initial_versions"][0]["client_side"] = json!("optional");
|
||||
json["initial_versions"][0]["server_side"] = json!("required");
|
||||
json["license_id"] = json!("LGPL-3.0-or-later");
|
||||
json["initial_versions"][0]["loaders"] = json!(["forge"]);
|
||||
json["initial_versions"][0]["game_versions"] = json!(["1.20.2"]);
|
||||
@ -203,7 +206,12 @@ async fn search_projects() {
|
||||
.get_project_deserialized(&format!("{test_name}-searchable-project-7"), USER_USER_PAT)
|
||||
.await;
|
||||
api.add_public_version(
|
||||
get_public_version_creation_data(project_7.id, "1.0.0", TestFile::build_random_jar()),
|
||||
get_public_version_creation_data(
|
||||
project_7.id,
|
||||
"1.0.0",
|
||||
TestFile::build_random_jar(),
|
||||
None::<fn(&mut serde_json::Value)>,
|
||||
),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
@ -226,21 +234,21 @@ async fn search_projects() {
|
||||
]),
|
||||
vec![1, 2, 3, 4],
|
||||
),
|
||||
(json!([["project_type:modpack"]]), vec![4]),
|
||||
(json!([["client_side:required"]]), vec![0, 2, 3]),
|
||||
(json!([["project_types:modpack"]]), vec![4]),
|
||||
(json!([["client_side:required"]]), vec![0, 2, 3, 7]),
|
||||
(json!([["server_side:required"]]), vec![0, 2, 3, 6, 7]),
|
||||
(json!([["open_source:true"]]), vec![0, 1, 2, 4, 5, 6, 7]),
|
||||
(json!([["license:MIT"]]), vec![1, 2, 4]),
|
||||
(json!([[r#"title:'Mysterious Project'"#]]), vec![2, 3]),
|
||||
(json!([["author:user"]]), vec![0, 1, 2, 4, 5, 7]),
|
||||
(json!([["versions:1.20.5"]]), vec![4, 5]),
|
||||
(json!([["game_versions:1.20.5"]]), vec![4, 5]),
|
||||
// bug fix
|
||||
(
|
||||
json!([
|
||||
// Only the forge one has 1.20.2, so its true that this project 'has'
|
||||
// 1.20.2 and a fabric version, but not true that it has a 1.20.2 fabric version.
|
||||
["categories:fabric"],
|
||||
["versions:1.20.2"]
|
||||
["game_versions:1.20.2"]
|
||||
]),
|
||||
vec![],
|
||||
),
|
||||
@ -255,7 +263,7 @@ async fn search_projects() {
|
||||
json!([
|
||||
["categories:mrpack"],
|
||||
["categories:fabric"],
|
||||
["project_type:modpack"]
|
||||
["project_types:modpack"]
|
||||
]),
|
||||
vec![4],
|
||||
),
|
||||
@ -266,6 +274,7 @@ async fn search_projects() {
|
||||
// - color (not varied)
|
||||
// - created_timestamp (not varied)
|
||||
// - modified_timestamp (not varied)
|
||||
// TODO: multiple different project types test
|
||||
|
||||
// Forcibly reset the search index
|
||||
let resp = api.reset_search_index().await;
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::common::environment::TestEnvironment;
|
||||
use common::environment::TestEnvironment;
|
||||
use std::collections::HashSet;
|
||||
|
||||
mod common;
|
||||
@ -8,40 +6,11 @@ mod common;
|
||||
#[actix_rt::test]
|
||||
async fn get_tags() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let game_versions = api.get_game_versions_deserialized().await;
|
||||
let loaders = api.get_loaders_deserialized().await;
|
||||
let side_types = api.get_side_types_deserialized().await;
|
||||
let categories = api.get_categories_deserialized().await;
|
||||
|
||||
// These tests match dummy data and will need to be updated if the dummy data changes
|
||||
// Versions should be ordered by:
|
||||
// - ordering
|
||||
// - ordering ties settled by date added to database
|
||||
// - We also expect presentation of NEWEST to OLDEST
|
||||
// - All null orderings are treated as older than any non-null ordering
|
||||
// (for this test, the 1.20.1, etc, versions are all null ordering)
|
||||
let game_version_versions = game_versions
|
||||
.into_iter()
|
||||
.map(|x| x.version)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
game_version_versions,
|
||||
[
|
||||
"Ordering_Negative1",
|
||||
"Ordering_Positive100",
|
||||
"1.20.5",
|
||||
"1.20.4",
|
||||
"1.20.3",
|
||||
"1.20.2",
|
||||
"1.20.1"
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect_vec()
|
||||
);
|
||||
|
||||
let loader_names = loaders.into_iter().map(|x| x.name).collect::<HashSet<_>>();
|
||||
assert_eq!(
|
||||
loader_names,
|
||||
@ -51,15 +20,6 @@ async fn get_tags() {
|
||||
.collect()
|
||||
);
|
||||
|
||||
let side_type_names = side_types.into_iter().collect::<HashSet<_>>();
|
||||
assert_eq!(
|
||||
side_type_names,
|
||||
["unknown", "required", "optional", "unsupported"]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
);
|
||||
|
||||
let category_names = categories
|
||||
.into_iter()
|
||||
.map(|x| x.name)
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
use crate::common::database::*;
|
||||
use crate::common::environment::TestEnvironment;
|
||||
use actix_web::test;
|
||||
use labrinth::models::teams::{OrganizationPermissions, ProjectPermissions};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::database::*;
|
||||
|
||||
use crate::common::environment::TestEnvironment;
|
||||
|
||||
// importing common module.
|
||||
mod common;
|
||||
|
||||
#[actix_rt::test]
|
||||
@ -30,8 +27,8 @@ async fn test_get_team() {
|
||||
] {
|
||||
// A non-member of the team should get basic info but not be able to see private data
|
||||
for uri in [
|
||||
format!("/v2/team/{team_id}/members"),
|
||||
format!("/v2/{team_association}/{team_association_id}/members"),
|
||||
format!("/v3/team/{team_id}/members"),
|
||||
format!("/v3/{team_association}/{team_association_id}/members"),
|
||||
] {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&uri)
|
||||
@ -49,7 +46,7 @@ async fn test_get_team() {
|
||||
// - not be able to see private data about the team, but see all members including themselves
|
||||
// - should not appear in the team members list to enemy users
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{team_id}/members"))
|
||||
.uri(&format!("/v3/team/{team_id}/members"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(&json!({
|
||||
"user_id": FRIEND_USER_ID,
|
||||
@ -59,8 +56,8 @@ async fn test_get_team() {
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
for uri in [
|
||||
format!("/v2/team/{team_id}/members"),
|
||||
format!("/v2/{team_association}/{team_association_id}/members"),
|
||||
format!("/v3/team/{team_id}/members"),
|
||||
format!("/v3/{team_association}/{team_association_id}/members"),
|
||||
] {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&uri)
|
||||
@ -99,15 +96,15 @@ async fn test_get_team() {
|
||||
// An accepted member of the team should appear in the team members list
|
||||
// and should be able to see private data about the team
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{team_id}/join"))
|
||||
.uri(&format!("/v3/team/{team_id}/join"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
for uri in [
|
||||
format!("/v2/team/{team_id}/members"),
|
||||
format!("/v2/{team_association}/{team_association_id}/members"),
|
||||
format!("/v3/team/{team_id}/members"),
|
||||
format!("/v3/{team_association}/{team_association_id}/members"),
|
||||
] {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&uri)
|
||||
@ -153,7 +150,7 @@ async fn test_get_team_project_orgs() {
|
||||
|
||||
// Attach alpha to zeta
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/organization/{zeta_organization_id}/projects"))
|
||||
.uri(&format!("/v3/organization/{zeta_organization_id}/projects"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"project_id": alpha_project_id,
|
||||
@ -164,7 +161,7 @@ async fn test_get_team_project_orgs() {
|
||||
|
||||
// Invite and add friend to zeta
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/members"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID,
|
||||
@ -174,7 +171,7 @@ async fn test_get_team_project_orgs() {
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/join"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/join"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -184,7 +181,7 @@ async fn test_get_team_project_orgs() {
|
||||
// - the members of the project team specifically
|
||||
// - not the ones from the organization
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -196,7 +193,7 @@ async fn test_get_team_project_orgs() {
|
||||
// The team members route from project should show:
|
||||
// - the members of the project team including the ones from the organization
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/project/{alpha_project_id}/members"))
|
||||
.uri(&format!("/v3/project/{alpha_project_id}/members"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -218,7 +215,7 @@ async fn test_patch_project_team_member() {
|
||||
|
||||
// Edit team as admin/mod but not a part of the team should be OK
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.set_json(json!({}))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.to_request();
|
||||
@ -227,7 +224,7 @@ async fn test_patch_project_team_member() {
|
||||
|
||||
// As a non-owner with full permissions, attempt to edit the owner's permissions/roles
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.set_json(json!({
|
||||
"role": "member"
|
||||
@ -237,7 +234,7 @@ async fn test_patch_project_team_member() {
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.set_json(json!({
|
||||
"permissions": 0
|
||||
@ -249,7 +246,7 @@ async fn test_patch_project_team_member() {
|
||||
|
||||
// Should not be able to edit organization permissions of a project team
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"organization_permissions": 0
|
||||
@ -264,7 +261,7 @@ async fn test_patch_project_team_member() {
|
||||
|
||||
// first, invite friend
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID,
|
||||
@ -276,7 +273,7 @@ async fn test_patch_project_team_member() {
|
||||
|
||||
// accept
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/join"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/join"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -284,7 +281,7 @@ async fn test_patch_project_team_member() {
|
||||
|
||||
// try to add permissions
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.set_json(json!({
|
||||
"permissions": (ProjectPermissions::EDIT_MEMBER | ProjectPermissions::EDIT_DETAILS).bits()
|
||||
@ -296,7 +293,7 @@ async fn test_patch_project_team_member() {
|
||||
// Cannot set a user to Owner
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
"/v3/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
@ -310,7 +307,7 @@ async fn test_patch_project_team_member() {
|
||||
for payout in [-1, 5001] {
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
"/v3/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
@ -325,7 +322,7 @@ async fn test_patch_project_team_member() {
|
||||
// Successful patch
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
"/v3/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.set_json(json!({
|
||||
@ -340,7 +337,7 @@ async fn test_patch_project_team_member() {
|
||||
|
||||
// Check results
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -373,7 +370,7 @@ async fn test_patch_organization_team_member() {
|
||||
|
||||
// Edit team as admin/mod but not a part of the team should be OK
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/members/{USER_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members/{USER_USER_ID}"))
|
||||
.set_json(json!({}))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.to_request();
|
||||
@ -382,7 +379,7 @@ async fn test_patch_organization_team_member() {
|
||||
|
||||
// As a non-owner with full permissions, attempt to edit the owner's permissions/roles
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/members/{USER_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members/{USER_USER_ID}"))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.set_json(json!({
|
||||
"role": "member"
|
||||
@ -392,7 +389,7 @@ async fn test_patch_organization_team_member() {
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/members/{USER_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members/{USER_USER_ID}"))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.set_json(json!({
|
||||
"permissions": 0
|
||||
@ -406,7 +403,7 @@ async fn test_patch_organization_team_member() {
|
||||
|
||||
// first, invite friend
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/members"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID,
|
||||
@ -417,7 +414,7 @@ async fn test_patch_organization_team_member() {
|
||||
|
||||
// accept
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/join"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/join"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -425,7 +422,7 @@ async fn test_patch_organization_team_member() {
|
||||
|
||||
// try to add permissions- fails, as we do not have EDIT_DETAILS
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.set_json(json!({
|
||||
"organization_permissions": (OrganizationPermissions::EDIT_MEMBER | OrganizationPermissions::EDIT_DETAILS).bits()
|
||||
@ -437,7 +434,7 @@ async fn test_patch_organization_team_member() {
|
||||
|
||||
// Cannot set a user to Owner
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"role": "Owner"
|
||||
@ -450,7 +447,7 @@ async fn test_patch_organization_team_member() {
|
||||
// Cannot set payouts outside of 0 and 5000
|
||||
for payout in [-1, 5001] {
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"payouts_split": payout
|
||||
@ -462,7 +459,7 @@ async fn test_patch_organization_team_member() {
|
||||
|
||||
// Successful patch
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.set_json(json!({
|
||||
"payouts_split": 51,
|
||||
@ -478,7 +475,7 @@ async fn test_patch_organization_team_member() {
|
||||
|
||||
// Check results
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/team/{zeta_team_id}/members"))
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -515,7 +512,7 @@ async fn transfer_ownership() {
|
||||
|
||||
// Cannot set friend as owner (not a member)
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/owner"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/owner"))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID
|
||||
}))
|
||||
@ -526,7 +523,7 @@ async fn transfer_ownership() {
|
||||
|
||||
// first, invite friend
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID,
|
||||
@ -537,7 +534,7 @@ async fn transfer_ownership() {
|
||||
|
||||
// accept
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/join"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/join"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
@ -545,7 +542,7 @@ async fn transfer_ownership() {
|
||||
|
||||
// Cannot set ourselves as owner
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/owner"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/owner"))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID
|
||||
}))
|
||||
@ -556,7 +553,7 @@ async fn transfer_ownership() {
|
||||
|
||||
// Can set friend as owner
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/owner"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/owner"))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID
|
||||
}))
|
||||
@ -567,7 +564,7 @@ async fn transfer_ownership() {
|
||||
|
||||
// Check
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/team/{alpha_team_id}/members"))
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members"))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID
|
||||
}))
|
||||
@ -599,7 +596,7 @@ async fn transfer_ownership() {
|
||||
// Confirm that user, a user who still has full permissions, cannot then remove the owner
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!(
|
||||
"/v2/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
"/v3/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
@ -620,7 +617,7 @@ async fn transfer_ownership() {
|
||||
// // This is because project-team permission overrriding must be possible, and this overriding can decrease the number of permissions a user has.
|
||||
|
||||
// let test_env = TestEnvironment::build(None).await;
|
||||
// let api = &test_env.v2;
|
||||
// let api = &test_env.v3;
|
||||
|
||||
// let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id;
|
||||
// let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id;
|
||||
|
||||
@ -3,7 +3,8 @@ use common::{
|
||||
environment::with_test_environment,
|
||||
};
|
||||
|
||||
use crate::common::{dummy_data::TestFile, request_data::get_public_project_creation_data};
|
||||
use crate::common::api_v3::request_data::get_public_project_creation_data;
|
||||
use common::dummy_data::TestFile;
|
||||
|
||||
mod common;
|
||||
|
||||
@ -19,7 +20,7 @@ mod common;
|
||||
#[actix_rt::test]
|
||||
pub async fn get_user_projects_after_creating_project_returns_new_project() {
|
||||
with_test_environment(|test_env| async move {
|
||||
let api = test_env.v2;
|
||||
let api = test_env.v3;
|
||||
api.get_user_projects_deserialized(USER_USER_ID, USER_USER_PAT)
|
||||
.await;
|
||||
|
||||
@ -41,7 +42,7 @@ pub async fn get_user_projects_after_creating_project_returns_new_project() {
|
||||
#[actix_rt::test]
|
||||
pub async fn get_user_projects_after_deleting_project_shows_removal() {
|
||||
with_test_environment(|test_env| async move {
|
||||
let api = test_env.v2;
|
||||
let api = test_env.v3;
|
||||
let (project, _) = api
|
||||
.add_public_project(
|
||||
get_public_project_creation_data("iota", Some(TestFile::BasicMod)),
|
||||
@ -67,7 +68,7 @@ pub async fn get_user_projects_after_joining_team_shows_team_projects() {
|
||||
with_test_environment(|test_env| async move {
|
||||
let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id;
|
||||
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id;
|
||||
let api = test_env.v2;
|
||||
let api = test_env.v3;
|
||||
api.get_user_projects_deserialized(FRIEND_USER_ID, FRIEND_USER_PAT)
|
||||
.await;
|
||||
|
||||
@ -90,7 +91,7 @@ pub async fn get_user_projects_after_leaving_team_shows_no_team_projects() {
|
||||
with_test_environment(|test_env| async move {
|
||||
let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id;
|
||||
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id;
|
||||
let api = test_env.v2;
|
||||
let api = test_env.v3;
|
||||
api.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
|
||||
.await;
|
||||
api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
|
||||
|
||||
538
tests/v2/project.rs
Normal file
538
tests/v2/project.rs
Normal file
@ -0,0 +1,538 @@
|
||||
use crate::common::{
|
||||
api_v2::request_data,
|
||||
database::{ENEMY_USER_PAT, FRIEND_USER_ID, FRIEND_USER_PAT, MOD_USER_PAT, USER_USER_PAT},
|
||||
dummy_data::{TestFile, DUMMY_CATEGORIES},
|
||||
environment::TestEnvironment,
|
||||
permissions::{PermissionsTest, PermissionsTestContext},
|
||||
};
|
||||
use actix_web::test;
|
||||
use itertools::Itertools;
|
||||
use labrinth::{
|
||||
database::models::project_item::PROJECTS_SLUGS_NAMESPACE,
|
||||
models::teams::ProjectPermissions,
|
||||
util::actix::{AppendsMultipart, MultipartSegment, MultipartSegmentData},
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_project_type_sanity() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
|
||||
// Perform all other patch tests on both 'mod' and 'modpack'
|
||||
let test_creation_mod = request_data::get_public_project_creation_data(
|
||||
"test-mod",
|
||||
Some(TestFile::build_random_jar()),
|
||||
);
|
||||
let test_creation_modpack = request_data::get_public_project_creation_data(
|
||||
"test-modpack",
|
||||
Some(TestFile::build_random_mrpack()),
|
||||
);
|
||||
for (mod_or_modpack, test_creation_data) in [
|
||||
("mod", test_creation_mod),
|
||||
("modpack", test_creation_modpack),
|
||||
] {
|
||||
let (test_project, test_version) = api
|
||||
.add_public_project(test_creation_data, USER_USER_PAT)
|
||||
.await;
|
||||
let test_project_slug = test_project.slug.as_ref().unwrap();
|
||||
|
||||
assert_eq!(test_project.project_type, mod_or_modpack);
|
||||
assert_eq!(test_project.loaders, vec!["fabric"]);
|
||||
assert_eq!(
|
||||
test_version[0].loaders.iter().map(|x| &x.0).collect_vec(),
|
||||
vec!["fabric"]
|
||||
);
|
||||
|
||||
let project = api
|
||||
.get_project_deserialized(test_project_slug, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(test_project.loaders, vec!["fabric"]);
|
||||
assert_eq!(project.project_type, mod_or_modpack);
|
||||
|
||||
let version = api
|
||||
.get_version_deserialized(&test_version[0].id.to_string(), USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(
|
||||
version.loaders.iter().map(|x| &x.0).collect_vec(),
|
||||
vec!["fabric"]
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: as we get more complicated strucures with v3 testing, and alpha/beta get more complicated, we should add more tests here,
|
||||
// to ensure that projects created with v3 routes are still valid and work with v3 routes.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_add_remove_project() {
|
||||
// Test setup and dummy data
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
|
||||
// Generate test project data.
|
||||
let mut json_data = json!(
|
||||
{
|
||||
"title": "Test_Add_Project project",
|
||||
"slug": "demo",
|
||||
"description": "Example description.",
|
||||
"body": "Example body.",
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"initial_versions": [{
|
||||
"file_parts": ["basic-mod.jar"],
|
||||
"version_number": "1.2.3",
|
||||
"version_title": "start",
|
||||
"dependencies": [],
|
||||
"game_versions": ["1.20.1"] ,
|
||||
"release_channel": "release",
|
||||
"loaders": ["fabric"],
|
||||
"featured": true
|
||||
}],
|
||||
"categories": [],
|
||||
"license_id": "MIT"
|
||||
}
|
||||
);
|
||||
|
||||
// Basic json
|
||||
let json_segment = MultipartSegment {
|
||||
name: "data".to_string(),
|
||||
filename: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
data: MultipartSegmentData::Text(serde_json::to_string(&json_data).unwrap()),
|
||||
};
|
||||
|
||||
// Basic json, with a different file
|
||||
json_data["initial_versions"][0]["file_parts"][0] = json!("basic-mod-different.jar");
|
||||
let json_diff_file_segment = MultipartSegment {
|
||||
data: MultipartSegmentData::Text(serde_json::to_string(&json_data).unwrap()),
|
||||
..json_segment.clone()
|
||||
};
|
||||
|
||||
// Basic json, with a different file, and a different slug
|
||||
json_data["slug"] = json!("new_demo");
|
||||
json_data["initial_versions"][0]["file_parts"][0] = json!("basic-mod-different.jar");
|
||||
let json_diff_slug_file_segment = MultipartSegment {
|
||||
data: MultipartSegmentData::Text(serde_json::to_string(&json_data).unwrap()),
|
||||
..json_segment.clone()
|
||||
};
|
||||
|
||||
// Basic file
|
||||
let file_segment = MultipartSegment {
|
||||
name: "basic-mod.jar".to_string(),
|
||||
filename: Some("basic-mod.jar".to_string()),
|
||||
content_type: Some("application/java-archive".to_string()),
|
||||
// TODO: look at these: can be simplified with TestFile
|
||||
data: MultipartSegmentData::Binary(
|
||||
include_bytes!("../../tests/files/basic-mod.jar").to_vec(),
|
||||
),
|
||||
};
|
||||
|
||||
// Differently named file, with the same content (for hash testing)
|
||||
let file_diff_name_segment = MultipartSegment {
|
||||
name: "basic-mod-different.jar".to_string(),
|
||||
filename: Some("basic-mod-different.jar".to_string()),
|
||||
content_type: Some("application/java-archive".to_string()),
|
||||
data: MultipartSegmentData::Binary(
|
||||
include_bytes!("../../tests/files/basic-mod.jar").to_vec(),
|
||||
),
|
||||
};
|
||||
|
||||
// Differently named file, with different content
|
||||
let file_diff_name_content_segment = MultipartSegment {
|
||||
name: "basic-mod-different.jar".to_string(),
|
||||
filename: Some("basic-mod-different.jar".to_string()),
|
||||
content_type: Some("application/java-archive".to_string()),
|
||||
data: MultipartSegmentData::Binary(
|
||||
include_bytes!("../../tests/files/basic-mod-different.jar").to_vec(),
|
||||
),
|
||||
};
|
||||
|
||||
// Add a project- simple, should work.
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
|
||||
let status = resp.status();
|
||||
assert_eq!(status, 200);
|
||||
|
||||
// Get the project we just made, and confirm that it's correct
|
||||
let project = api.get_project_deserialized("demo", USER_USER_PAT).await;
|
||||
assert!(project.versions.len() == 1);
|
||||
let uploaded_version_id = project.versions[0];
|
||||
|
||||
// Checks files to ensure they were uploaded and correctly identify the file
|
||||
let hash = sha1::Sha1::from(include_bytes!("../../tests/files/basic-mod.jar"))
|
||||
.digest()
|
||||
.to_string();
|
||||
let version = api
|
||||
.get_version_from_hash_deserialized(&hash, "sha1", USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(version.id, uploaded_version_id);
|
||||
|
||||
// Reusing with a different slug and the same file should fail
|
||||
// Even if that file is named differently
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![
|
||||
json_diff_slug_file_segment.clone(), // Different slug, different file name
|
||||
file_diff_name_segment.clone(), // Different file name, same content
|
||||
])
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Reusing with the same slug and a different file should fail
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![
|
||||
json_diff_file_segment.clone(), // Same slug, different file name
|
||||
file_diff_name_content_segment.clone(), // Different file name, different content
|
||||
])
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Different slug, different file should succeed
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![
|
||||
json_diff_slug_file_segment.clone(), // Different slug, different file name
|
||||
file_diff_name_content_segment.clone(), // Different file name, same content
|
||||
])
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
// Get
|
||||
let project = api.get_project_deserialized("demo", USER_USER_PAT).await;
|
||||
let id = project.id.to_string();
|
||||
|
||||
// Remove the project
|
||||
let resp = test_env.v2.remove_project("demo", USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Confirm that the project is gone from the cache
|
||||
assert_eq!(
|
||||
test_env
|
||||
.db
|
||||
.redis_pool
|
||||
.get::<i64, _>(PROJECTS_SLUGS_NAMESPACE, "demo")
|
||||
.await
|
||||
.unwrap(),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
test_env
|
||||
.db
|
||||
.redis_pool
|
||||
.get::<i64, _>(PROJECTS_SLUGS_NAMESPACE, id)
|
||||
.await
|
||||
.unwrap(),
|
||||
None
|
||||
);
|
||||
|
||||
// Old slug no longer works
|
||||
let resp = api.get_project("demo", USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 404);
|
||||
|
||||
// Cleanup test db
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn permissions_upload_version() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id;
|
||||
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id;
|
||||
let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id;
|
||||
let alpha_file_hash = &test_env.dummy.as_ref().unwrap().project_alpha.file_hash;
|
||||
|
||||
let upload_version = ProjectPermissions::UPLOAD_VERSION;
|
||||
|
||||
// Upload version with basic-mod.jar
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::post().uri("/v2/version").set_multipart([
|
||||
MultipartSegment {
|
||||
name: "data".to_string(),
|
||||
filename: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
data: MultipartSegmentData::Text(
|
||||
serde_json::to_string(&json!({
|
||||
"project_id": ctx.project_id.unwrap(),
|
||||
"file_parts": ["basic-mod.jar"],
|
||||
"version_number": "1.0.0",
|
||||
"version_title": "1.0.0",
|
||||
"version_type": "release",
|
||||
"dependencies": [],
|
||||
"game_versions": ["1.20.1"],
|
||||
"loaders": ["fabric"],
|
||||
"featured": false,
|
||||
|
||||
}))
|
||||
.unwrap(),
|
||||
),
|
||||
},
|
||||
MultipartSegment {
|
||||
name: "basic-mod.jar".to_string(),
|
||||
filename: Some("basic-mod.jar".to_string()),
|
||||
content_type: Some("application/java-archive".to_string()),
|
||||
data: MultipartSegmentData::Binary(
|
||||
include_bytes!("../../tests/files/basic-mod.jar").to_vec(),
|
||||
),
|
||||
},
|
||||
])
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
.simple_project_permissions_test(upload_version, req_gen)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Upload file to existing version
|
||||
// Uses alpha project, as it has an existing version
|
||||
let req_gen = |_: &PermissionsTestContext| {
|
||||
test::TestRequest::post()
|
||||
.uri(&format!("/v2/version/{}/file", alpha_version_id))
|
||||
.set_multipart([
|
||||
MultipartSegment {
|
||||
name: "data".to_string(),
|
||||
filename: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
data: MultipartSegmentData::Text(
|
||||
serde_json::to_string(&json!({
|
||||
"file_parts": ["basic-mod-different.jar"],
|
||||
}))
|
||||
.unwrap(),
|
||||
),
|
||||
},
|
||||
MultipartSegment {
|
||||
name: "basic-mod-different.jar".to_string(),
|
||||
filename: Some("basic-mod-different.jar".to_string()),
|
||||
content_type: Some("application/java-archive".to_string()),
|
||||
data: MultipartSegmentData::Binary(
|
||||
include_bytes!("../../tests/files/basic-mod-different.jar").to_vec(),
|
||||
),
|
||||
},
|
||||
])
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
.with_existing_project(alpha_project_id, alpha_team_id)
|
||||
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
||||
.simple_project_permissions_test(upload_version, req_gen)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Patch version
|
||||
// Uses alpha project, as it has an existing version
|
||||
let req_gen = |_: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/version/{}", alpha_version_id))
|
||||
.set_json(json!({
|
||||
"name": "Basic Mod",
|
||||
}))
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
.with_existing_project(alpha_project_id, alpha_team_id)
|
||||
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
||||
.simple_project_permissions_test(upload_version, req_gen)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Delete version file
|
||||
// Uses alpha project, as it has an existing version
|
||||
let delete_version = ProjectPermissions::DELETE_VERSION;
|
||||
let req_gen = |_: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!("/v2/version_file/{}", alpha_file_hash))
|
||||
};
|
||||
|
||||
PermissionsTest::new(&test_env)
|
||||
.with_existing_project(alpha_project_id, alpha_team_id)
|
||||
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
||||
.simple_project_permissions_test(delete_version, req_gen)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Delete version
|
||||
// Uses alpha project, as it has an existing version
|
||||
let req_gen = |_: &PermissionsTestContext| {
|
||||
test::TestRequest::delete().uri(&format!("/v2/version/{}", alpha_version_id))
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
.with_existing_project(alpha_project_id, alpha_team_id)
|
||||
.with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true)
|
||||
.simple_project_permissions_test(delete_version, req_gen)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
pub async fn test_patch_project() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
|
||||
let alpha_project_slug = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug;
|
||||
let beta_project_slug = &test_env.dummy.as_ref().unwrap().project_beta.project_slug;
|
||||
|
||||
// First, we do some patch requests that should fail.
|
||||
// Failure because the user is not authorized.
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"title": "Test_Add_Project project - test 1",
|
||||
}),
|
||||
ENEMY_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// Failure because we are setting URL fields to invalid urls.
|
||||
for url_type in ["issues_url", "source_url", "wiki_url", "discord_url"] {
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
url_type: "w.fake.url",
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
}
|
||||
|
||||
// Failure because these are illegal requested statuses for a normal user.
|
||||
for req in ["unknown", "processing", "withheld", "scheduled"] {
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"requested_status": req,
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
}
|
||||
|
||||
// Failure because these should not be able to be set by a non-mod
|
||||
for key in ["moderation_message", "moderation_message_body"] {
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
key: "test",
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// (should work for a mod, though)
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
key: "test",
|
||||
}),
|
||||
MOD_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
}
|
||||
|
||||
// Failed patch to alpha slug:
|
||||
// - slug collision with beta
|
||||
// - too short slug
|
||||
// - too long slug
|
||||
// - not url safe slug
|
||||
// - not url safe slug
|
||||
for slug in [
|
||||
beta_project_slug,
|
||||
"a",
|
||||
&"a".repeat(100),
|
||||
"not url safe%&^!#$##!@#$%^&*()",
|
||||
] {
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"slug": slug, // the other dummy project has this slug
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
}
|
||||
|
||||
// Not allowed to directly set status, as 'beta_project_slug' (the other project) is "processing" and cannot have its status changed like this.
|
||||
let resp = api
|
||||
.edit_project(
|
||||
beta_project_slug,
|
||||
json!({
|
||||
"status": "private"
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// Sucessful request to patch many fields.
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"slug": "newslug",
|
||||
"title": "New successful title",
|
||||
"description": "New successful description",
|
||||
"body": "New successful body",
|
||||
"categories": [DUMMY_CATEGORIES[0]],
|
||||
"license_id": "MIT",
|
||||
"issues_url": "https://github.com",
|
||||
"discord_url": "https://discord.gg",
|
||||
"wiki_url": "https://wiki.com",
|
||||
"client_side": "optional",
|
||||
"server_side": "required",
|
||||
"donation_urls": [{
|
||||
"id": "patreon",
|
||||
"platform": "Patreon",
|
||||
"url": "https://patreon.com"
|
||||
}]
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Old slug no longer works
|
||||
let resp = api.get_project(alpha_project_slug, USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 404);
|
||||
|
||||
// New slug does work
|
||||
let project = api.get_project_deserialized("newslug", USER_USER_PAT).await;
|
||||
|
||||
assert_eq!(project.slug.unwrap(), "newslug");
|
||||
assert_eq!(project.title, "New successful title");
|
||||
assert_eq!(project.description, "New successful description");
|
||||
assert_eq!(project.body, "New successful body");
|
||||
assert_eq!(project.categories, vec![DUMMY_CATEGORIES[0]]);
|
||||
assert_eq!(project.license.id, "MIT");
|
||||
assert_eq!(project.issues_url, Some("https://github.com".to_string()));
|
||||
assert_eq!(project.discord_url, Some("https://discord.gg".to_string()));
|
||||
assert_eq!(project.wiki_url, Some("https://wiki.com".to_string()));
|
||||
assert_eq!(project.client_side.as_str(), "optional");
|
||||
assert_eq!(project.server_side.as_str(), "required");
|
||||
assert_eq!(project.donation_urls.unwrap()[0].url, "https://patreon.com");
|
||||
|
||||
// Cleanup test db
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
109
tests/v2/scopes.rs
Normal file
109
tests/v2/scopes.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use crate::common::environment::TestEnvironment;
|
||||
use crate::common::scopes::ScopeTest;
|
||||
use actix_web::test;
|
||||
use labrinth::models::pats::Scopes;
|
||||
use labrinth::util::actix::AppendsMultipart;
|
||||
use labrinth::util::actix::MultipartSegment;
|
||||
use labrinth::util::actix::MultipartSegmentData;
|
||||
|
||||
use serde_json::json;
|
||||
// Project version creation scopes
|
||||
#[actix_rt::test]
|
||||
pub async fn project_version_create_scopes() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
|
||||
// Create project
|
||||
let create_project = Scopes::PROJECT_CREATE;
|
||||
let json_data = json!(
|
||||
{
|
||||
"title": "Test_Add_Project project",
|
||||
"slug": "demo",
|
||||
"description": "Example description.",
|
||||
"body": "Example body.",
|
||||
"initial_versions": [{
|
||||
"file_parts": ["basic-mod.jar"],
|
||||
"version_number": "1.2.3",
|
||||
"version_title": "start",
|
||||
"dependencies": [],
|
||||
"game_versions": ["1.20.1"] ,
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"release_channel": "release",
|
||||
"loaders": ["fabric"],
|
||||
"featured": true
|
||||
}],
|
||||
"categories": [],
|
||||
"license_id": "MIT"
|
||||
}
|
||||
);
|
||||
let json_segment = MultipartSegment {
|
||||
name: "data".to_string(),
|
||||
filename: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
data: MultipartSegmentData::Text(serde_json::to_string(&json_data).unwrap()),
|
||||
};
|
||||
let file_segment = MultipartSegment {
|
||||
name: "basic-mod.jar".to_string(),
|
||||
filename: Some("basic-mod.jar".to_string()),
|
||||
content_type: Some("application/java-archive".to_string()),
|
||||
data: MultipartSegmentData::Binary(
|
||||
include_bytes!("../../tests/files/basic-mod.jar").to_vec(),
|
||||
),
|
||||
};
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri("/v3/project")
|
||||
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
|
||||
};
|
||||
let (_, success) = ScopeTest::new(&test_env)
|
||||
.test(req_gen, create_project)
|
||||
.await
|
||||
.unwrap();
|
||||
let project_id = success["id"].as_str().unwrap();
|
||||
|
||||
// Add version to project
|
||||
let create_version = Scopes::VERSION_CREATE;
|
||||
let json_data = json!(
|
||||
{
|
||||
"project_id": project_id,
|
||||
"file_parts": ["basic-mod-different.jar"],
|
||||
"version_number": "1.2.3.4",
|
||||
"version_title": "start",
|
||||
"dependencies": [],
|
||||
"game_versions": ["1.20.1"] ,
|
||||
"client_side": "required",
|
||||
"server_side": "optional",
|
||||
"release_channel": "release",
|
||||
"loaders": ["fabric"],
|
||||
"featured": true
|
||||
}
|
||||
);
|
||||
let json_segment = MultipartSegment {
|
||||
name: "data".to_string(),
|
||||
filename: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
data: MultipartSegmentData::Text(serde_json::to_string(&json_data).unwrap()),
|
||||
};
|
||||
let file_segment = MultipartSegment {
|
||||
name: "basic-mod-different.jar".to_string(),
|
||||
filename: Some("basic-mod.jar".to_string()),
|
||||
content_type: Some("application/java-archive".to_string()),
|
||||
data: MultipartSegmentData::Binary(
|
||||
include_bytes!("../../tests/files/basic-mod-different.jar").to_vec(),
|
||||
),
|
||||
};
|
||||
|
||||
let req_gen = || {
|
||||
test::TestRequest::post()
|
||||
.uri("/v3/version")
|
||||
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
|
||||
};
|
||||
ScopeTest::new(&test_env)
|
||||
.test(req_gen, create_version)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Cleanup test db
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
299
tests/v2/search.rs
Normal file
299
tests/v2/search.rs
Normal file
@ -0,0 +1,299 @@
|
||||
use crate::common::api_v2::request_data;
|
||||
use crate::common::api_v2::request_data::get_public_version_creation_data;
|
||||
use crate::common::api_v2::request_data::ProjectCreationRequestData;
|
||||
use crate::common::database::*;
|
||||
use crate::common::dummy_data::TestFile;
|
||||
use crate::common::dummy_data::DUMMY_CATEGORIES;
|
||||
use crate::common::environment::TestEnvironment;
|
||||
use futures::stream::StreamExt;
|
||||
use labrinth::models::ids::base62_impl::parse_base62;
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_projects() {
|
||||
// TODO: ("Match changes in the 2 version of thee add_public_version_creation_data to those made in v3
|
||||
// It should drastically simplify this function
|
||||
|
||||
// Test setup and dummy data
|
||||
let test_env = TestEnvironment::build(Some(8)).await;
|
||||
let api = &test_env.v2;
|
||||
let test_name = test_env.db.database_name.clone();
|
||||
|
||||
// Add dummy projects of various categories for searchability
|
||||
let mut project_creation_futures = vec![];
|
||||
|
||||
let create_async_future =
|
||||
|id: u64,
|
||||
pat: &'static str,
|
||||
is_modpack: bool,
|
||||
modify_json: Box<dyn Fn(&mut serde_json::Value)>| {
|
||||
let slug = format!("{test_name}-searchable-project-{id}");
|
||||
|
||||
let jar = if is_modpack {
|
||||
TestFile::build_random_mrpack()
|
||||
} else {
|
||||
TestFile::build_random_jar()
|
||||
};
|
||||
let mut basic_project_json =
|
||||
request_data::get_public_project_creation_data_json(&slug, Some(&jar));
|
||||
modify_json(&mut basic_project_json);
|
||||
|
||||
let basic_project_multipart =
|
||||
request_data::get_public_creation_data_multipart(&basic_project_json, Some(&jar));
|
||||
// Add a project- simple, should work.
|
||||
let req = api.add_public_project(
|
||||
ProjectCreationRequestData {
|
||||
slug,
|
||||
jar: Some(jar),
|
||||
segment_data: basic_project_multipart,
|
||||
},
|
||||
pat,
|
||||
);
|
||||
async move {
|
||||
let (project, _) = req.await;
|
||||
|
||||
// Approve, so that the project is searchable
|
||||
let resp = api
|
||||
.edit_project(
|
||||
&project.id.to_string(),
|
||||
json!({
|
||||
"status": "approved"
|
||||
}),
|
||||
MOD_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
(project.id.0, id)
|
||||
}
|
||||
};
|
||||
|
||||
// Test project 0
|
||||
let id = 0;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[4..6]);
|
||||
json["server_side"] = json!("required");
|
||||
json["license_id"] = json!("LGPL-3.0-or-later");
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
id,
|
||||
USER_USER_PAT,
|
||||
false,
|
||||
Box::new(modify_json),
|
||||
));
|
||||
|
||||
// Test project 1
|
||||
let id = 1;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[0..2]);
|
||||
json["client_side"] = json!("optional");
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
id,
|
||||
USER_USER_PAT,
|
||||
false,
|
||||
Box::new(modify_json),
|
||||
));
|
||||
|
||||
// Test project 2
|
||||
let id = 2;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[0..2]);
|
||||
json["server_side"] = json!("required");
|
||||
json["title"] = json!("Mysterious Project");
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
id,
|
||||
USER_USER_PAT,
|
||||
false,
|
||||
Box::new(modify_json),
|
||||
));
|
||||
|
||||
// Test project 3
|
||||
let id = 3;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[0..3]);
|
||||
json["server_side"] = json!("required");
|
||||
json["initial_versions"][0]["game_versions"] = json!(["1.20.4"]);
|
||||
json["title"] = json!("Mysterious Project");
|
||||
json["license_id"] = json!("LicenseRef-All-Rights-Reserved"); // closed source
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
id,
|
||||
FRIEND_USER_PAT,
|
||||
false,
|
||||
Box::new(modify_json),
|
||||
));
|
||||
|
||||
// Test project 4
|
||||
let id = 4;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[0..3]);
|
||||
json["client_side"] = json!("optional");
|
||||
json["initial_versions"][0]["game_versions"] = json!(["1.20.5"]);
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
id,
|
||||
USER_USER_PAT,
|
||||
true,
|
||||
Box::new(modify_json),
|
||||
));
|
||||
|
||||
// Test project 5
|
||||
let id = 5;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[5..6]);
|
||||
json["client_side"] = json!("optional");
|
||||
json["initial_versions"][0]["game_versions"] = json!(["1.20.5"]);
|
||||
json["license_id"] = json!("LGPL-3.0-or-later");
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
id,
|
||||
USER_USER_PAT,
|
||||
false,
|
||||
Box::new(modify_json),
|
||||
));
|
||||
|
||||
// Test project 6
|
||||
let id = 6;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[5..6]);
|
||||
json["client_side"] = json!("optional");
|
||||
json["server_side"] = json!("required");
|
||||
json["license_id"] = json!("LGPL-3.0-or-later");
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
id,
|
||||
FRIEND_USER_PAT,
|
||||
false,
|
||||
Box::new(modify_json),
|
||||
));
|
||||
|
||||
// Test project 7 (testing the search bug)
|
||||
// This project has an initial private forge version that is 1.20.3, and a fabric 1.20.5 version.
|
||||
// This means that a search for fabric + 1.20.3 or forge + 1.20.5 should not return this project.
|
||||
let id = 7;
|
||||
let modify_json = |json: &mut serde_json::Value| {
|
||||
json["categories"] = json!(DUMMY_CATEGORIES[5..6]);
|
||||
json["client_side"] = json!("optional");
|
||||
json["server_side"] = json!("required");
|
||||
json["license_id"] = json!("LGPL-3.0-or-later");
|
||||
json["initial_versions"][0]["loaders"] = json!(["forge"]);
|
||||
json["initial_versions"][0]["game_versions"] = json!(["1.20.2"]);
|
||||
};
|
||||
project_creation_futures.push(create_async_future(
|
||||
id,
|
||||
USER_USER_PAT,
|
||||
false,
|
||||
Box::new(modify_json),
|
||||
));
|
||||
|
||||
// Await all project creation
|
||||
// Returns a mapping of:
|
||||
// project id -> test id
|
||||
let id_conversion: Arc<HashMap<u64, u64>> = Arc::new(
|
||||
futures::future::join_all(project_creation_futures)
|
||||
.await
|
||||
.into_iter()
|
||||
.collect(),
|
||||
);
|
||||
|
||||
// Create a second version for project 7
|
||||
let project_7 = api
|
||||
.get_project_deserialized(&format!("{test_name}-searchable-project-7"), USER_USER_PAT)
|
||||
.await;
|
||||
api.add_public_version(
|
||||
get_public_version_creation_data(project_7.id, "1.0.0", TestFile::build_random_jar()),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Pairs of:
|
||||
// 1. vec of search facets
|
||||
// 2. expected project ids to be returned by this search
|
||||
let pairs = vec![
|
||||
(json!([["categories:fabric"]]), vec![0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
(json!([["categories:forge"]]), vec![7]),
|
||||
(
|
||||
json!([["categories:fabric", "categories:forge"]]),
|
||||
vec![0, 1, 2, 3, 4, 5, 6, 7],
|
||||
),
|
||||
(json!([["categories:fabric"], ["categories:forge"]]), vec![]),
|
||||
(
|
||||
json!([
|
||||
["categories:fabric"],
|
||||
[&format!("categories:{}", DUMMY_CATEGORIES[0])],
|
||||
]),
|
||||
vec![1, 2, 3, 4],
|
||||
),
|
||||
(json!([["project_types:modpack"]]), vec![4]),
|
||||
(json!([["client_side:required"]]), vec![0, 2, 3, 7]),
|
||||
(json!([["server_side:required"]]), vec![0, 2, 3, 6, 7]),
|
||||
(json!([["open_source:true"]]), vec![0, 1, 2, 4, 5, 6, 7]),
|
||||
(json!([["license:MIT"]]), vec![1, 2, 4]),
|
||||
(json!([[r#"title:'Mysterious Project'"#]]), vec![2, 3]),
|
||||
(json!([["author:user"]]), vec![0, 1, 2, 4, 5, 7]),
|
||||
(json!([["versions:1.20.5"]]), vec![4, 5]),
|
||||
// bug fix
|
||||
(
|
||||
json!([
|
||||
// Only the forge one has 1.20.2, so its true that this project 'has'
|
||||
// 1.20.2 and a fabric version, but not true that it has a 1.20.2 fabric version.
|
||||
["categories:fabric"],
|
||||
["versions:1.20.2"]
|
||||
]),
|
||||
vec![],
|
||||
),
|
||||
// Project type change
|
||||
// Modpack should still be able to search based on former loader, even though technically the loader is 'mrpack'
|
||||
(json!([["categories:mrpack"]]), vec![4]),
|
||||
(
|
||||
json!([["categories:mrpack"], ["categories:fabric"]]),
|
||||
vec![4],
|
||||
),
|
||||
(
|
||||
json!([
|
||||
["categories:mrpack"],
|
||||
["categories:fabric"],
|
||||
["project_type:modpack"]
|
||||
]),
|
||||
vec![4],
|
||||
),
|
||||
];
|
||||
|
||||
// TODO: Untested:
|
||||
// - downloads (not varied)
|
||||
// - color (not varied)
|
||||
// - created_timestamp (not varied)
|
||||
// - modified_timestamp (not varied)
|
||||
|
||||
// Forcibly reset the search index
|
||||
let resp = api.reset_search_index().await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Test searches
|
||||
let stream = futures::stream::iter(pairs);
|
||||
stream
|
||||
.for_each_concurrent(1, |(facets, mut expected_project_ids)| {
|
||||
let id_conversion = id_conversion.clone();
|
||||
let test_name = test_name.clone();
|
||||
async move {
|
||||
let projects = api
|
||||
.search_deserialized(Some(&test_name), Some(facets.clone()), USER_USER_PAT)
|
||||
.await;
|
||||
let mut found_project_ids: Vec<u64> = projects
|
||||
.hits
|
||||
.into_iter()
|
||||
.map(|p| id_conversion[&parse_base62(&p.project_id).unwrap()])
|
||||
.collect();
|
||||
expected_project_ids.sort();
|
||||
found_project_ids.sort();
|
||||
assert_eq!(found_project_ids, expected_project_ids);
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
// Cleanup test db
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
74
tests/v2/tags.rs
Normal file
74
tests/v2/tags.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use crate::common::environment::TestEnvironment;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn get_tags() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
|
||||
let game_versions = api.get_game_versions_deserialized().await;
|
||||
let loaders = api.get_loaders_deserialized().await;
|
||||
let side_types = api.get_side_types_deserialized().await;
|
||||
let categories = api.get_categories_deserialized().await;
|
||||
|
||||
// These tests match dummy data and will need to be updated if the dummy data changes;
|
||||
let game_version_versions = game_versions
|
||||
.into_iter()
|
||||
.map(|x| x.version)
|
||||
.collect::<HashSet<_>>();
|
||||
assert_eq!(
|
||||
game_version_versions,
|
||||
[
|
||||
"1.20.1",
|
||||
"1.20.2",
|
||||
"1.20.3",
|
||||
"1.20.4",
|
||||
"1.20.5",
|
||||
"Ordering_Negative1",
|
||||
"Ordering_Positive100"
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
);
|
||||
|
||||
let loader_names = loaders.into_iter().map(|x| x.name).collect::<HashSet<_>>();
|
||||
assert_eq!(
|
||||
loader_names,
|
||||
["fabric", "forge", "mrpack"]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
);
|
||||
|
||||
let side_type_names = side_types.into_iter().collect::<HashSet<_>>();
|
||||
assert_eq!(
|
||||
side_type_names,
|
||||
["unknown", "required", "optional", "unsupported"]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
);
|
||||
|
||||
let category_names = categories
|
||||
.into_iter()
|
||||
.map(|x| x.name)
|
||||
.collect::<HashSet<_>>();
|
||||
assert_eq!(
|
||||
category_names,
|
||||
[
|
||||
"combat",
|
||||
"economy",
|
||||
"food",
|
||||
"optimization",
|
||||
"decoration",
|
||||
"mobs",
|
||||
"magic"
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
);
|
||||
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
409
tests/v2/version.rs
Normal file
409
tests/v2/version.rs
Normal file
@ -0,0 +1,409 @@
|
||||
use actix_web::test;
|
||||
use futures::StreamExt;
|
||||
use labrinth::models::projects::{ProjectId, VersionId};
|
||||
use labrinth::{
|
||||
models::{
|
||||
ids::base62_impl::parse_base62,
|
||||
projects::{Loader, VersionStatus, VersionType},
|
||||
},
|
||||
routes::v2::version_file::FileUpdateData,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::api_v2::request_data::get_public_version_creation_data;
|
||||
use crate::common::{
|
||||
database::{ENEMY_USER_PAT, USER_USER_PAT},
|
||||
dummy_data::TestFile,
|
||||
environment::TestEnvironment,
|
||||
};
|
||||
|
||||
#[actix_rt::test]
|
||||
pub async fn test_patch_version() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
|
||||
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id;
|
||||
|
||||
// // First, we do some patch requests that should fail.
|
||||
// // Failure because the user is not authorized.
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"name": "test 1",
|
||||
}),
|
||||
ENEMY_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// Failure because these are illegal requested statuses for a normal user.
|
||||
for req in ["unknown", "scheduled"] {
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"status": req,
|
||||
// requested status it not set here, but in /schedule
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
}
|
||||
|
||||
// Sucessful request to patch many fields.
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"name": "new version name",
|
||||
"version_number": "1.3.0",
|
||||
"changelog": "new changelog",
|
||||
"version_type": "beta",
|
||||
// // "dependencies": [], TODO: test this
|
||||
"game_versions": ["1.20.5"],
|
||||
"loaders": ["forge"],
|
||||
"featured": false,
|
||||
// "primary_file": [], TODO: test this
|
||||
// // "downloads": 0, TODO: moderator exclusive
|
||||
"status": "draft",
|
||||
// // "filetypes": ["jar"], TODO: test this
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
let version = api
|
||||
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(version.name, "new version name");
|
||||
assert_eq!(version.version_number, "1.3.0");
|
||||
assert_eq!(version.changelog, "new changelog");
|
||||
assert_eq!(
|
||||
version.version_type,
|
||||
serde_json::from_str::<VersionType>("\"beta\"").unwrap()
|
||||
);
|
||||
assert_eq!(version.game_versions, vec!["1.20.5"]);
|
||||
assert_eq!(version.loaders, vec![Loader("forge".to_string())]);
|
||||
assert!(!version.featured);
|
||||
assert_eq!(version.status, VersionStatus::from_string("draft"));
|
||||
|
||||
// These ones are checking the v2-v3 rerouting, we eneusre that only 'game_versions'
|
||||
// works as expected, as well as only 'loaders'
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"game_versions": ["1.20.1", "1.20.2", "1.20.4"],
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
let version = api
|
||||
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(version.game_versions, vec!["1.20.1", "1.20.2", "1.20.4"]);
|
||||
assert_eq!(version.loaders, vec![Loader("forge".to_string())]); // From last patch
|
||||
|
||||
let resp = api
|
||||
.edit_version(
|
||||
alpha_version_id,
|
||||
json!({
|
||||
"loaders": ["fabric"],
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
let version = api
|
||||
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(version.game_versions, vec!["1.20.1", "1.20.2", "1.20.4"]); // From last patch
|
||||
assert_eq!(version.loaders, vec![Loader("fabric".to_string())]);
|
||||
|
||||
// Cleanup test db
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn version_updates() {
|
||||
// Test setup and dummy data
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
|
||||
let alpha_project_id: &String = &test_env.dummy.as_ref().unwrap().project_alpha.project_id;
|
||||
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id;
|
||||
let beta_version_id = &test_env.dummy.as_ref().unwrap().project_beta.version_id;
|
||||
let alpha_version_hash = &test_env.dummy.as_ref().unwrap().project_alpha.file_hash;
|
||||
let beta_version_hash = &test_env.dummy.as_ref().unwrap().project_beta.file_hash;
|
||||
|
||||
// Quick test, using get version from hash
|
||||
let version = api
|
||||
.get_version_from_hash_deserialized(alpha_version_hash, "sha1", USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(&version.id.to_string(), alpha_version_id);
|
||||
|
||||
// Get versions from hash
|
||||
let versions = api
|
||||
.get_versions_from_hashes_deserialized(
|
||||
&[alpha_version_hash.as_str(), beta_version_hash.as_str()],
|
||||
"sha1",
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(versions.len(), 2);
|
||||
assert_eq!(
|
||||
&versions[alpha_version_hash].id.to_string(),
|
||||
alpha_version_id
|
||||
);
|
||||
assert_eq!(&versions[beta_version_hash].id.to_string(), beta_version_id);
|
||||
|
||||
// When there is only the one version, there should be no updates
|
||||
let version = api
|
||||
.get_update_from_hash_deserialized(
|
||||
alpha_version_hash,
|
||||
"sha1",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(&version.id.to_string(), alpha_version_id);
|
||||
|
||||
let versions = api
|
||||
.update_files_deserialized(
|
||||
"sha1",
|
||||
vec![alpha_version_hash.to_string()],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(versions.len(), 1);
|
||||
assert_eq!(
|
||||
&versions[alpha_version_hash].id.to_string(),
|
||||
alpha_version_id
|
||||
);
|
||||
|
||||
// Add 3 new versions, 1 before, and 2 after, with differing game_version/version_types/loaders
|
||||
let mut update_ids = vec![];
|
||||
for (version_number, patch_value) in [
|
||||
(
|
||||
"0.9.9",
|
||||
json!({
|
||||
"game_versions": ["1.20.1"],
|
||||
}),
|
||||
),
|
||||
(
|
||||
"1.5.0",
|
||||
json!({
|
||||
"game_versions": ["1.20.3"],
|
||||
"loaders": ["fabric"],
|
||||
}),
|
||||
),
|
||||
(
|
||||
"1.5.1",
|
||||
json!({
|
||||
"game_versions": ["1.20.4"],
|
||||
"loaders": ["forge"],
|
||||
"version_type": "beta"
|
||||
}),
|
||||
),
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
let version = api
|
||||
.add_public_version(
|
||||
get_public_version_creation_data(
|
||||
ProjectId(parse_base62(alpha_project_id).unwrap()),
|
||||
version_number,
|
||||
TestFile::build_random_jar(),
|
||||
),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
update_ids.push(version.id);
|
||||
|
||||
// Patch using json
|
||||
api.edit_version(&version.id.to_string(), patch_value.clone(), USER_USER_PAT)
|
||||
.await;
|
||||
}
|
||||
|
||||
let check_expected = |game_versions: Option<Vec<String>>,
|
||||
loaders: Option<Vec<String>>,
|
||||
version_types: Option<Vec<String>>,
|
||||
result_id: Option<VersionId>| async move {
|
||||
let (success, result_id) = match result_id {
|
||||
Some(id) => (true, id),
|
||||
None => (false, VersionId(0)),
|
||||
};
|
||||
// get_update_from_hash
|
||||
let resp = api
|
||||
.get_update_from_hash(
|
||||
alpha_version_hash,
|
||||
"sha1",
|
||||
loaders.clone(),
|
||||
game_versions.clone(),
|
||||
version_types.clone(),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
if success {
|
||||
assert_eq!(resp.status(), 200);
|
||||
let body: serde_json::Value = test::read_body_json(resp).await;
|
||||
let id = body["id"].as_str().unwrap();
|
||||
assert_eq!(id, &result_id.to_string());
|
||||
} else {
|
||||
assert_eq!(resp.status(), 404);
|
||||
}
|
||||
|
||||
// update_files
|
||||
let versions = api
|
||||
.update_files_deserialized(
|
||||
"sha1",
|
||||
vec![alpha_version_hash.to_string()],
|
||||
loaders.clone(),
|
||||
game_versions.clone(),
|
||||
version_types.clone(),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
if success {
|
||||
assert_eq!(versions.len(), 1);
|
||||
let first = versions.iter().next().unwrap();
|
||||
assert_eq!(first.1.id, result_id);
|
||||
} else {
|
||||
assert_eq!(versions.len(), 0);
|
||||
}
|
||||
|
||||
// update_individual_files
|
||||
let hashes = vec![FileUpdateData {
|
||||
hash: alpha_version_hash.to_string(),
|
||||
loaders,
|
||||
game_versions,
|
||||
version_types: version_types.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|v| serde_json::from_str(&format!("\"{v}\"")).unwrap())
|
||||
.collect()
|
||||
}),
|
||||
}];
|
||||
let versions = api
|
||||
.update_individual_files_deserialized("sha1", hashes, USER_USER_PAT)
|
||||
.await;
|
||||
if success {
|
||||
assert_eq!(versions.len(), 1);
|
||||
let first = versions.iter().next().unwrap();
|
||||
assert_eq!(first.1.id, result_id);
|
||||
} else {
|
||||
assert_eq!(versions.len(), 0);
|
||||
}
|
||||
};
|
||||
|
||||
let tests = vec![
|
||||
check_expected(
|
||||
Some(vec!["1.20.1".to_string()]),
|
||||
None,
|
||||
None,
|
||||
Some(update_ids[0]),
|
||||
),
|
||||
check_expected(
|
||||
Some(vec!["1.20.3".to_string()]),
|
||||
None,
|
||||
None,
|
||||
Some(update_ids[1]),
|
||||
),
|
||||
check_expected(
|
||||
Some(vec!["1.20.4".to_string()]),
|
||||
None,
|
||||
None,
|
||||
Some(update_ids[2]),
|
||||
),
|
||||
// Loader restrictions
|
||||
check_expected(
|
||||
None,
|
||||
Some(vec!["fabric".to_string()]),
|
||||
None,
|
||||
Some(update_ids[1]),
|
||||
),
|
||||
check_expected(
|
||||
None,
|
||||
Some(vec!["forge".to_string()]),
|
||||
None,
|
||||
Some(update_ids[2]),
|
||||
),
|
||||
// Version type restrictions
|
||||
check_expected(
|
||||
None,
|
||||
None,
|
||||
Some(vec!["release".to_string()]),
|
||||
Some(update_ids[1]),
|
||||
),
|
||||
check_expected(
|
||||
None,
|
||||
None,
|
||||
Some(vec!["beta".to_string()]),
|
||||
Some(update_ids[2]),
|
||||
),
|
||||
// Specific combination
|
||||
check_expected(
|
||||
None,
|
||||
Some(vec!["fabric".to_string()]),
|
||||
Some(vec!["release".to_string()]),
|
||||
Some(update_ids[1]),
|
||||
),
|
||||
// Impossible combination
|
||||
check_expected(
|
||||
None,
|
||||
Some(vec!["fabric".to_string()]),
|
||||
Some(vec!["beta".to_string()]),
|
||||
None,
|
||||
),
|
||||
// No restrictions, should do the last one
|
||||
check_expected(None, None, None, Some(update_ids[2])),
|
||||
];
|
||||
|
||||
// Wait on all tests, 4 at a time
|
||||
futures::stream::iter(tests)
|
||||
.buffer_unordered(4)
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
|
||||
// We do a couple small tests for get_project_versions_deserialized as well
|
||||
// TODO: expand this more.
|
||||
let versions = api
|
||||
.get_project_versions_deserialized(
|
||||
alpha_project_id,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(versions.len(), 4);
|
||||
let versions = api
|
||||
.get_project_versions_deserialized(
|
||||
alpha_project_id,
|
||||
None,
|
||||
Some(vec!["forge".to_string()]),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(versions.len(), 1);
|
||||
|
||||
// Cleanup test db
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
16
tests/v2_tests.rs
Normal file
16
tests/v2_tests.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// importing common module.
|
||||
mod common;
|
||||
|
||||
// Not all tests expect exactly the same functionality in v2 and v3.
|
||||
// For example, though we expect the /GET version to return the corresponding project,
|
||||
// we may want to do different checks for each.
|
||||
// (such as checking client_side in v2, but loader fields on v3- which are model-exclusie)
|
||||
|
||||
// Such V2 tests are exported here
|
||||
mod v2 {
|
||||
mod project;
|
||||
mod scopes;
|
||||
mod search;
|
||||
mod tags;
|
||||
mod version;
|
||||
}
|
||||
@ -1,16 +1,18 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use actix_web::test;
|
||||
use common::environment::TestEnvironment;
|
||||
use futures::StreamExt;
|
||||
use labrinth::database::models::version_item::VERSIONS_NAMESPACE;
|
||||
use labrinth::models::ids::base62_impl::parse_base62;
|
||||
use labrinth::models::projects::{Loader, ProjectId, VersionId, VersionStatus, VersionType};
|
||||
use labrinth::routes::v2::version_file::FileUpdateData;
|
||||
use labrinth::routes::v3::version_file::FileUpdateData;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::api_v3::request_data::get_public_version_creation_data;
|
||||
use crate::common::database::*;
|
||||
|
||||
use crate::common::dummy_data::TestFile;
|
||||
use crate::common::request_data::get_public_version_creation_data;
|
||||
|
||||
// importing common module.
|
||||
mod common;
|
||||
@ -19,7 +21,7 @@ mod common;
|
||||
async fn test_get_version() {
|
||||
// Test setup and dummy data
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
let alpha_project_id: &String = &test_env.dummy.as_ref().unwrap().project_alpha.project_id;
|
||||
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id;
|
||||
let beta_version_id = &test_env.dummy.as_ref().unwrap().project_beta.version_id;
|
||||
@ -68,11 +70,10 @@ async fn test_get_version() {
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
|
||||
async fn version_updates() {
|
||||
// Test setup and dummy data
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let alpha_project_id: &String = &test_env.dummy.as_ref().unwrap().project_alpha.project_id;
|
||||
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id;
|
||||
@ -158,11 +159,12 @@ async fn version_updates() {
|
||||
.iter()
|
||||
{
|
||||
let version = api
|
||||
.add_public_version(
|
||||
.add_public_version_deserialized(
|
||||
get_public_version_creation_data(
|
||||
ProjectId(parse_base62(alpha_project_id).unwrap()),
|
||||
version_number,
|
||||
TestFile::build_random_jar(),
|
||||
None::<fn(&mut serde_json::Value)>,
|
||||
),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
@ -222,10 +224,21 @@ async fn version_updates() {
|
||||
}
|
||||
|
||||
// update_individual_files
|
||||
let mut loader_fields = HashMap::new();
|
||||
if let Some(game_versions) = game_versions {
|
||||
loader_fields.insert(
|
||||
"game_versions".to_string(),
|
||||
game_versions
|
||||
.into_iter()
|
||||
.map(|v| json!(v))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
let hashes = vec![FileUpdateData {
|
||||
hash: alpha_version_hash.to_string(),
|
||||
loaders,
|
||||
game_versions,
|
||||
loader_fields: Some(loader_fields),
|
||||
version_types: version_types.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|v| serde_json::from_str(&format!("\"{v}\"")).unwrap())
|
||||
@ -349,7 +362,7 @@ async fn version_updates() {
|
||||
#[actix_rt::test]
|
||||
pub async fn test_patch_version() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
|
||||
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id;
|
||||
|
||||
@ -414,7 +427,6 @@ pub async fn test_patch_version() {
|
||||
version.version_type,
|
||||
serde_json::from_str::<VersionType>("\"beta\"").unwrap()
|
||||
);
|
||||
assert_eq!(version.game_versions, vec!["1.20.5"]);
|
||||
assert_eq!(version.loaders, vec![Loader("forge".to_string())]);
|
||||
assert!(!version.featured);
|
||||
assert_eq!(version.status, VersionStatus::from_string("draft"));
|
||||
@ -435,7 +447,6 @@ pub async fn test_patch_version() {
|
||||
let version = api
|
||||
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(version.game_versions, vec!["1.20.1", "1.20.2", "1.20.4"]);
|
||||
assert_eq!(version.loaders, vec![Loader("forge".to_string())]); // From last patch
|
||||
|
||||
let resp = api
|
||||
@ -452,7 +463,6 @@ pub async fn test_patch_version() {
|
||||
let version = api
|
||||
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(version.game_versions, vec!["1.20.1", "1.20.2", "1.20.4"]); // From last patch
|
||||
assert_eq!(version.loaders, vec![Loader("fabric".to_string())]);
|
||||
|
||||
// Cleanup test db
|
||||
@ -462,7 +472,7 @@ pub async fn test_patch_version() {
|
||||
#[actix_rt::test]
|
||||
pub async fn test_project_versions() {
|
||||
let test_env = TestEnvironment::build(None).await;
|
||||
let api = &test_env.v2;
|
||||
let api = &test_env.v3;
|
||||
let alpha_project_id: &String = &test_env.dummy.as_ref().unwrap().project_alpha.project_id;
|
||||
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id;
|
||||
let _beta_version_id = &test_env.dummy.as_ref().unwrap().project_beta.version_id;
|
||||
@ -498,14 +508,14 @@ async fn can_create_version_with_ordering() {
|
||||
let alpha_project_id = env.dummy.as_ref().unwrap().project_alpha.project_id.clone();
|
||||
|
||||
let new_version_id = get_json_val_str(
|
||||
env.v2
|
||||
env.v3
|
||||
.create_default_version(&alpha_project_id, Some(1), USER_USER_PAT)
|
||||
.await
|
||||
.id,
|
||||
);
|
||||
|
||||
let versions = env
|
||||
.v2
|
||||
.v3
|
||||
.get_versions(vec![new_version_id.clone()], USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(versions[0].ordering, Some(1));
|
||||
@ -519,13 +529,13 @@ async fn edit_version_ordering_works() {
|
||||
let alpha_version_id = env.dummy.as_ref().unwrap().project_alpha.version_id.clone();
|
||||
|
||||
let resp = env
|
||||
.v2
|
||||
.v3
|
||||
.edit_version_ordering(&alpha_version_id, Some(10), USER_USER_PAT)
|
||||
.await;
|
||||
assert_status(&resp, StatusCode::NO_CONTENT);
|
||||
|
||||
let versions = env
|
||||
.v2
|
||||
.v3
|
||||
.get_versions(vec![alpha_version_id.clone()], USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(versions[0].ordering, Some(10));
|
||||
@ -539,17 +549,17 @@ async fn version_ordering_for_specified_orderings_orders_lower_order_first() {
|
||||
let alpha_project_id = env.dummy.as_ref().unwrap().project_alpha.project_id.clone();
|
||||
let alpha_version_id = env.dummy.as_ref().unwrap().project_alpha.version_id.clone();
|
||||
let new_version_id = get_json_val_str(
|
||||
env.v2
|
||||
env.v3
|
||||
.create_default_version(&alpha_project_id, Some(1), USER_USER_PAT)
|
||||
.await
|
||||
.id,
|
||||
);
|
||||
env.v2
|
||||
env.v3
|
||||
.edit_version_ordering(&alpha_version_id, Some(10), USER_USER_PAT)
|
||||
.await;
|
||||
|
||||
let versions = env
|
||||
.v2
|
||||
.v3
|
||||
.get_versions(
|
||||
vec![alpha_version_id.clone(), new_version_id.clone()],
|
||||
USER_USER_PAT,
|
||||
@ -566,14 +576,14 @@ async fn version_ordering_when_unspecified_orders_oldest_first() {
|
||||
let alpha_project_id = &env.dummy.as_ref().unwrap().project_alpha.project_id.clone();
|
||||
let alpha_version_id = env.dummy.as_ref().unwrap().project_alpha.version_id.clone();
|
||||
let new_version_id = get_json_val_str(
|
||||
env.v2
|
||||
env.v3
|
||||
.create_default_version(alpha_project_id, None, USER_USER_PAT)
|
||||
.await
|
||||
.id,
|
||||
);
|
||||
|
||||
let versions = env
|
||||
.v2
|
||||
.v3
|
||||
.get_versions(
|
||||
vec![alpha_version_id.clone(), new_version_id.clone()],
|
||||
USER_USER_PAT,
|
||||
@ -590,17 +600,17 @@ async fn version_ordering_when_specified_orders_specified_before_unspecified() {
|
||||
let alpha_project_id = &env.dummy.as_ref().unwrap().project_alpha.project_id.clone();
|
||||
let alpha_version_id = env.dummy.as_ref().unwrap().project_alpha.version_id.clone();
|
||||
let new_version_id = get_json_val_str(
|
||||
env.v2
|
||||
env.v3
|
||||
.create_default_version(alpha_project_id, Some(10000), USER_USER_PAT)
|
||||
.await
|
||||
.id,
|
||||
);
|
||||
env.v2
|
||||
env.v3
|
||||
.edit_version_ordering(&alpha_version_id, None, USER_USER_PAT)
|
||||
.await;
|
||||
|
||||
let versions = env
|
||||
.v2
|
||||
.v3
|
||||
.get_versions(
|
||||
vec![alpha_version_id.clone(), new_version_id.clone()],
|
||||
USER_USER_PAT,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user