|
| 1 | +use crate::models::{CrateOwner, OwnerKind}; |
1 | 2 | use crate::tests::builders::CrateBuilder;
|
2 | 3 | use crate::tests::util::{RequestHelper, TestApp};
|
| 4 | +use crate::util::diesel::prelude::*; |
| 5 | +use crates_io_database::schema::{crate_owners, users}; |
3 | 6 | use http::StatusCode;
|
4 | 7 | use insta::assert_snapshot;
|
5 | 8 |
|
@@ -80,3 +83,71 @@ async fn test_unknown_team() {
|
80 | 83 | assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
81 | 84 | assert_snapshot!(response.text(), @r#"{"errors":[{"detail":"could not find team with login `github:unknown:unknown`"}]}"#);
|
82 | 85 | }
|
| 86 | + |
| 87 | +/// See <https://github.com/rust-lang/crates.io/issues/2736>. |
| 88 | +#[tokio::test(flavor = "multi_thread")] |
| 89 | +async fn test_issue_2736() -> anyhow::Result<()> { |
| 90 | + use diesel::RunQueryDsl; |
| 91 | + |
| 92 | + let (app, _) = TestApp::full().empty(); |
| 93 | + let mut conn = app.db_conn(); |
| 94 | + |
| 95 | + // - A user had a GitHub account named, let's say, `foo` |
| 96 | + let foo1 = app.db_new_user("foo"); |
| 97 | + |
| 98 | + // - Another user `someone_else` added them as an owner of a crate |
| 99 | + let someone_else = app.db_new_user("someone_else"); |
| 100 | + |
| 101 | + let krate = CrateBuilder::new("crate1", someone_else.as_model().id).expect_build(&mut conn); |
| 102 | + |
| 103 | + diesel::insert_into(crate_owners::table) |
| 104 | + .values(CrateOwner { |
| 105 | + crate_id: krate.id, |
| 106 | + owner_id: foo1.as_model().id, |
| 107 | + created_by: someone_else.as_model().id, |
| 108 | + owner_kind: OwnerKind::User, |
| 109 | + email_notifications: true, |
| 110 | + }) |
| 111 | + .execute(&mut conn)?; |
| 112 | + |
| 113 | + // - `foo` deleted their GitHub account (but crates.io has no real knowledge of this) |
| 114 | + // - `foo` recreated their GitHub account with the same username (because it was still available), but in this situation GitHub assigns them a new ID |
| 115 | + // - When `foo` now logs in to crates.io, it's a different account than their old `foo` crates.io account because of the new GitHub ID (and if it wasn't, this would be a security problem) |
| 116 | + let foo2 = app.db_new_user("foo"); |
| 117 | + |
| 118 | + let github_ids = users::table |
| 119 | + .filter(users::gh_login.eq("foo")) |
| 120 | + .select(users::gh_id) |
| 121 | + .load::<i32>(&mut conn)?; |
| 122 | + |
| 123 | + assert_eq!(github_ids.len(), 2); |
| 124 | + assert_ne!(github_ids[0], github_ids[1]); |
| 125 | + |
| 126 | + // - The new `foo` account is NOT an owner of the crate |
| 127 | + let owner_ids = crate_owners::table |
| 128 | + .filter(crate_owners::crate_id.eq(krate.id)) |
| 129 | + .filter(crate_owners::deleted.eq(false)) |
| 130 | + .select(crate_owners::owner_id) |
| 131 | + .load::<i32>(&mut conn)?; |
| 132 | + |
| 133 | + assert_eq!(owner_ids.len(), 2); |
| 134 | + assert!(!owner_ids.contains(&foo2.as_model().id)); |
| 135 | + |
| 136 | + // - `someone_else` can't add the new `foo` account as an owner, nor can they remove the old `foo` as an owner :( |
| 137 | + let url = "/api/v1/crates/crate1/owners"; |
| 138 | + let payload = json!({ "owners": ["foo"] }).to_string(); |
| 139 | + let response = someone_else.delete_with_body::<()>(url, payload).await; |
| 140 | + assert_eq!(response.status(), StatusCode::OK); |
| 141 | + assert_snapshot!(response.text(), @r#"{"msg":"owners successfully removed","ok":true}"#); |
| 142 | + |
| 143 | + let owner_ids = crate_owners::table |
| 144 | + .filter(crate_owners::crate_id.eq(krate.id)) |
| 145 | + .filter(crate_owners::deleted.eq(false)) |
| 146 | + .select(crate_owners::owner_id) |
| 147 | + .load::<i32>(&mut conn)?; |
| 148 | + |
| 149 | + assert_eq!(owner_ids.len(), 1); |
| 150 | + assert_eq!(owner_ids[0], someone_else.as_model().id); |
| 151 | + |
| 152 | + Ok(()) |
| 153 | +} |
0 commit comments