Skip to content

Commit

Permalink
api tests
Browse files Browse the repository at this point in the history
  • Loading branch information
phiresky committed Feb 7, 2025
1 parent 55e71b9 commit 339d2f4
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 42 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions api_tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@
"scripts": {
"lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'",
"fix": "prettier --write src && eslint --fix src",
"api-test": "jest -i follow.spec.ts && jest -i image.spec.ts && jest -i user.spec.ts && jest -i private_message.spec.ts && jest -i community.spec.ts && jest -i private_community.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts ",
"api-test": "jest -i follow.spec.ts && jest -i image.spec.ts && jest -i user.spec.ts && jest -i private_message.spec.ts && jest -i community.spec.ts && jest -i private_community.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts && jest -i tags.spec.ts",
"api-test-follow": "jest -i follow.spec.ts",
"api-test-comment": "jest -i comment.spec.ts",
"api-test-post": "jest -i post.spec.ts",
"api-test-user": "jest -i user.spec.ts",
"api-test-community": "jest -i community.spec.ts",
"api-test-private-community": "jest -i private_community.spec.ts",
"api-test-private-message": "jest -i private_message.spec.ts",
"api-test-image": "jest -i image.spec.ts"
"api-test-image": "jest -i image.spec.ts",
"api-test-tags": "jest -i tags.spec.ts"
},
"devDependencies": {
"@types/jest": "^29.5.12",
Expand Down
155 changes: 155 additions & 0 deletions api_tests/src/tags.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
jest.setTimeout(120000);

import {
alpha,
beta,
setupLogins,
createCommunity,
unfollows,
randomString,
createPost,
} from "./shared";
import { CreateCommunityTag } from "lemmy-js-client/dist/types/CreateCommunityTag";
import { UpdateCommunityTag } from "lemmy-js-client/dist/types/UpdateCommunityTag";
import { DeleteCommunityTag } from "lemmy-js-client/dist/types/DeleteCommunityTag";
import { ListCommunityTags } from "lemmy-js-client/dist/types/ListCommunityTags";
import { UpdatePostTags } from "lemmy-js-client/dist/types/UpdatePostTags";

beforeAll(setupLogins);
afterAll(unfollows);

test("Create, update, delete community tag", async () => {
// Create a community first
let communityRes = await createCommunity(alpha);
const communityId = communityRes.community_view.community.id;

// Create a tag
const tagName = randomString(10);
const tagSlug = tagName.toLowerCase();
let createForm: CreateCommunityTag = {
name: tagName,
id_slug: tagSlug,
community_id: communityId,
};
let createRes = await alpha.createCommunityTag(createForm);
expect(createRes.id).toBeDefined();
expect(createRes.name).toBe(tagName);
expect(createRes.community_id).toBe(communityId);

// Update the tag
const newTagName = randomString(10);
let updateForm: UpdateCommunityTag = {
tag_id: createRes.id,
name: newTagName,
};
let updateRes = await alpha.updateCommunityTag(updateForm);
expect(updateRes.id).toBe(createRes.id);
expect(updateRes.name).toBe(newTagName);
expect(updateRes.community_id).toBe(communityId);

// List tags
let listForm: ListCommunityTags = {
community_id: communityId,
};
let listRes = await alpha.listCommunityTags(listForm);
expect(listRes.tags.length).toBeGreaterThan(0);
expect(listRes.tags.find(t => t.id === createRes.id)?.name).toBe(newTagName);

// Delete the tag
let deleteForm: DeleteCommunityTag = {
tag_id: createRes.id,
};
let deleteRes = await alpha.deleteCommunityTag(deleteForm);
expect(deleteRes.id).toBe(createRes.id);

// Verify tag is deleted
listRes = await alpha.listCommunityTags(listForm);
expect(listRes.tags.find(t => t.id === createRes.id)).toBeUndefined();
});

test("Update post tags", async () => {
// Create a community
let communityRes = await createCommunity(alpha);
const communityId = communityRes.community_view.community.id;

// Create two tags
const tag1Name = randomString(10);
const tag1Slug = tag1Name.toLowerCase();
let createForm1: CreateCommunityTag = {
name: tag1Name,
id_slug: tag1Slug,
community_id: communityId,
};
let tag1Res = await alpha.createCommunityTag(createForm1);
expect(tag1Res.id).toBeDefined();

const tag2Name = randomString(10);
const tag2Slug = tag2Name.toLowerCase();
let createForm2: CreateCommunityTag = {
name: tag2Name,
id_slug: tag2Slug,
community_id: communityId,
};
let tag2Res = await alpha.createCommunityTag(createForm2);
expect(tag2Res.id).toBeDefined();

// Create a post
let postRes = await alpha.createPost({
name: randomString(10),
community_id: communityId,
});
expect(postRes.post_view.post.id).toBeDefined();

// Update post tags
let updateForm: UpdatePostTags = {
post_id: postRes.post_view.post.id,
tags: [tag1Res.id, tag2Res.id],
};
let updateRes = await alpha.updatePostTags(updateForm);
expect(updateRes.post_view.post.id).toBe(postRes.post_view.post.id);
expect(updateRes.post_view.tags?.length).toBe(2);
expect(updateRes.post_view.tags?.map(t => t.id).sort()).toEqual([tag1Res.id, tag2Res.id].sort());

// Update post to remove one tag
updateForm.tags = [tag1Res.id];
updateRes = await alpha.updatePostTags(updateForm);
expect(updateRes.post_view.post.id).toBe(postRes.post_view.post.id);
expect(updateRes.post_view.tags?.length).toBe(1);
expect(updateRes.post_view.tags?.[0].id).toBe(tag1Res.id);
});


test("Post author can update post tags", async () => {
// Create a community
let communityRes = await createCommunity(alpha);
const communityId = communityRes.community_view.community.id;

// Create a tag
const tagName = randomString(10);
const tagSlug = tagName.toLowerCase();
let createForm: CreateCommunityTag = {
name: tagName,
id_slug: tagSlug,
community_id: communityId,
};
let tagRes = await alpha.createCommunityTag(createForm);
expect(tagRes.id).toBeDefined();

let postRes = await createPost(
alpha,
communityId,
"https://example.com/",
"post with tags",
);
expect(postRes.post_view.post.id).toBeDefined();

// Beta should be able to update tags on their own post
let updateForm: UpdatePostTags = {
post_id: postRes.post_view.post.id,
tags: [tagRes.id],
};
let updateRes = await alpha.updatePostTags(updateForm);
expect(updateRes.post_view.post.id).toBe(postRes.post_view.post.id);
expect(updateRes.post_view.tags?.length).toBe(1);
expect(updateRes.post_view.tags?.[0].id).toBe(tagRes.id);
});
1 change: 1 addition & 0 deletions crates/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ lemmy_db_schema = { workspace = true, features = ["full"] }
lemmy_db_views = { workspace = true, features = ["full"] }
lemmy_api_common = { workspace = true, features = ["full"] }
activitypub_federation = { workspace = true }
tracing = { workspace = true }
bcrypt = { workspace = true }
actix-web = { workspace = true }
base64 = { workspace = true }
Expand Down
8 changes: 6 additions & 2 deletions crates/api/src/community/tag.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use activitypub_federation::config::Data;
use actix_web::web::Json;
use actix_web::web::{Json, Query};
use lemmy_api_common::{
community::{
CommunityTagResponse,
Expand Down Expand Up @@ -56,6 +56,7 @@ pub async fn create_community_tag(

Ok(Json(CommunityTagResponse {
id: tag.id,
ap_id: tag.ap_id,
name: tag.name,
community_id: tag.community_id,
}))
Expand Down Expand Up @@ -93,6 +94,7 @@ pub async fn update_community_tag(

Ok(Json(CommunityTagResponse {
id: tag.id,
ap_id: tag.ap_id,
name: tag.name,
community_id: tag.community_id,
}))
Expand Down Expand Up @@ -130,14 +132,15 @@ pub async fn delete_community_tag(

Ok(Json(CommunityTagResponse {
id: tag.id,
ap_id: tag.ap_id,
name: tag.name,
community_id: tag.community_id,
}))
}

#[tracing::instrument(skip(context))]
pub async fn list_community_tags(
data: Json<ListCommunityTags>,
data: Query<ListCommunityTags>,
context: Data<LemmyContext>,
) -> LemmyResult<Json<ListCommunityTagsResponse>> {
let tags = Tag::get_by_community(&mut context.pool(), data.community_id).await?;
Expand All @@ -146,6 +149,7 @@ pub async fn list_community_tags(
.into_iter()
.map(|t| CommunityTagResponse {
id: t.id,
ap_id: t.ap_id,
name: t.name,
community_id: t.community_id,
})
Expand Down
24 changes: 8 additions & 16 deletions crates/api/src/post/tags.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
build_response::build_post_response,
context::LemmyContext,
post::{UpdatePostTags, UpdatePostTagsResponse},
post::{PostResponse, UpdatePostTags},
utils::check_community_mod_action,
};
use lemmy_db_schema::{
source::{community::Community, post::Post, post_tag::PostTag, tag::PostTagInsertForm},
traits::Crud,
};
use lemmy_db_views::structs::{LocalUserView, PostView};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyResult;

#[tracing::instrument(skip(context))]
pub async fn update_post_tags(
data: Json<UpdatePostTags>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<UpdatePostTagsResponse>> {
) -> LemmyResult<Json<PostResponse>> {
let post = Post::read(&mut context.pool(), data.post_id).await?;
let community = Community::read(&mut context.pool(), post.community_id).await?;


let is_author = local_user_view.person.id == post.creator_id;

if !is_author {
let community = Community::read(&mut context.pool(), post.community_id).await?;
// Check if user is either the post author or a community mod
check_community_mod_action(
&local_user_view.person,
Expand All @@ -46,14 +47,5 @@ pub async fn update_post_tags(
PostTag::create(&mut context.pool(), &form).await?;
}

// Get updated post view
let post_view = PostView::read(
&mut context.pool(),
data.post_id,
Some(&local_user_view.local_user),
false,
)
.await?;

Ok(Json(UpdatePostTagsResponse { post_view }))
build_post_response(&context, post.community_id, local_user_view, data.post_id).await
}
3 changes: 2 additions & 1 deletion crates/api_common/src/community.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use lemmy_db_schema::{
newtypes::{CommunityId, LanguageId, PersonId, TagId},
newtypes::{CommunityId, DbUrl, LanguageId, PersonId, TagId},
source::site::Site,
CommunityVisibility,
ListingType,
Expand Down Expand Up @@ -32,6 +32,7 @@ pub struct CreateCommunityTag {
#[cfg_attr(feature = "full", ts(export))]
pub struct CommunityTagResponse {
pub id: TagId,
pub ap_id: DbUrl,
pub name: String,
pub community_id: CommunityId,
}
Expand Down
6 changes: 0 additions & 6 deletions crates/api_common/src/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@ pub struct UpdatePostTags {
pub tags: Vec<TagId>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS))]
#[cfg_attr(feature = "full", ts(export))]
pub struct UpdatePostTagsResponse {
pub post_view: PostView,
}

#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
Expand Down
1 change: 0 additions & 1 deletion crates/api_crud/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ actix-web = { workspace = true }
url = { workspace = true }
futures.workspace = true
uuid = { workspace = true }
tracing = { workspace = true }
anyhow.workspace = true
chrono.workspace = true
accept-language = "3.1.0"
Expand Down
2 changes: 1 addition & 1 deletion crates/db_views/src/post/post_tags_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl FromSql<Nullable<sql_types::Json>, Pg> for PostTags {
) -> diesel::deserialize::Result<Self> {
match bytes {
Some(bytes) => Self::from_sql(bytes),
None => Ok(Self { tags: vec![] }),
None => Ok(Self(vec![])),
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions crates/db_views/src/post/post_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2294,14 +2294,14 @@ mod tests {
)
.await?;

assert_eq!(2, post_view.tags.tags.len());
assert_eq!(data.tag_1.name, post_view.tags.tags[0].name);
assert_eq!(data.tag_2.name, post_view.tags.tags[1].name);
assert_eq!(2, post_view.tags.0.len());
assert_eq!(data.tag_1.name, post_view.tags.0[0].name);
assert_eq!(data.tag_2.name, post_view.tags.0[1].name);

let all_posts = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(2, all_posts[0].tags.tags.len()); // post with tags
assert_eq!(0, all_posts[1].tags.tags.len()); // bot post
assert_eq!(0, all_posts[2].tags.tags.len()); // normal post
assert_eq!(2, all_posts[0].tags.0.len()); // post with tags
assert_eq!(0, all_posts[1].tags.0.len()); // bot post
assert_eq!(0, all_posts[2].tags.0.len()); // normal post

Ok(())
}
Expand Down
7 changes: 3 additions & 4 deletions crates/db_views/src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1168,10 +1168,9 @@ pub enum SearchCombinedView {
}

#[derive(Clone, serde::Serialize, serde::Deserialize, Debug, PartialEq, Default)]
#[cfg_attr(feature = "full", derive(TS, FromSqlRow, AsExpression))]
#[serde(transparent)]
#[cfg_attr(feature = "full", derive(TS, FromSqlRow, AsExpression))]
#[cfg_attr(feature = "full", diesel(sql_type = Nullable<sql_types::Json>))]
/// we wrap this in a struct so we can implement FromSqlRow<Json> for it
pub struct PostTags {
pub tags: Vec<Tag>,
}
pub struct PostTags(pub Vec<Tag>);

4 changes: 2 additions & 2 deletions src/api_routes_v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
.route("/list", get().to(list_communities))
.route("/follow", post().to(follow_community))
.route("/delete", post().to(delete_community))
.route("/post_tags", get().to(list_community_tags))
.route("/post_tag/list", get().to(list_community_tags))
// Mod Actions
.route("/remove", post().to(remove_community))
.route("/transfer", post().to(transfer_community))
Expand Down Expand Up @@ -250,7 +250,7 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) {
.route("", put().to(update_post))
.route("/delete", post().to(delete_post))
.route("/remove", post().to(remove_post))
.route("/update_tags", post().to(update_post_tags))
.route("/tags", put().to(update_post_tags))
.route("/mark_as_read", post().to(mark_post_as_read))
.route("/mark_as_read/many", post().to(mark_posts_as_read))
.route("/hide", post().to(hide_post))
Expand Down

0 comments on commit 339d2f4

Please sign in to comment.