|
18 | 18 | package bridge_test |
19 | 19 |
|
20 | 20 | import ( |
| 21 | + "bytes" |
21 | 22 | "context" |
22 | 23 | "crypto/tls" |
23 | 24 | "fmt" |
24 | 25 | "net" |
| 26 | + "os" |
25 | 27 | "strings" |
26 | 28 | "testing" |
27 | 29 | "time" |
@@ -217,3 +219,114 @@ func TestBridge_SendDraftFlags(t *testing.T) { |
217 | 219 | }) |
218 | 220 | }) |
219 | 221 | } |
| 222 | + |
| 223 | +func TestBridge_SendInvite(t *testing.T) { |
| 224 | + withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) { |
| 225 | + // Create a recipient user. |
| 226 | + _, _, err := s.CreateUser("recipient", password) |
| 227 | + require.NoError(t, err) |
| 228 | + |
| 229 | + // Set "attach public keys" to true for the user. |
| 230 | + withClient(ctx, t, s, username, password, func(ctx context.Context, client *proton.Client) { |
| 231 | + settings, err := client.SetAttachPublicKey(ctx, proton.SetAttachPublicKeyReq{AttachPublicKey: true}) |
| 232 | + require.NoError(t, err) |
| 233 | + require.Equal(t, proton.Bool(true), settings.AttachPublicKey) |
| 234 | + }) |
| 235 | + |
| 236 | + // The sender should be fully synced. |
| 237 | + withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, _ *bridge.Mocks) { |
| 238 | + syncCh, done := chToType[events.Event, events.SyncFinished](bridge.GetEvents(events.SyncFinished{})) |
| 239 | + defer done() |
| 240 | + |
| 241 | + userID, err := bridge.LoginFull(ctx, username, password, nil, nil) |
| 242 | + require.NoError(t, err) |
| 243 | + |
| 244 | + require.Equal(t, userID, (<-syncCh).UserID) |
| 245 | + }) |
| 246 | + |
| 247 | + // Start the bridge. |
| 248 | + withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, _ *bridge.Mocks) { |
| 249 | + // Get the sender user info. |
| 250 | + userInfo, err := bridge.QueryUserInfo(username) |
| 251 | + require.NoError(t, err) |
| 252 | + |
| 253 | + // Connect the sender IMAP client. |
| 254 | + imapClient, err := client.Dial(net.JoinHostPort(constants.Host, fmt.Sprint(bridge.GetIMAPPort()))) |
| 255 | + require.NoError(t, err) |
| 256 | + require.NoError(t, imapClient.Login(userInfo.Addresses[0], string(userInfo.BridgePass))) |
| 257 | + defer imapClient.Logout() //nolint:errcheck |
| 258 | + |
| 259 | + // The message to send. |
| 260 | + b, err := os.ReadFile("testdata/invite.eml") |
| 261 | + require.NoError(t, err) |
| 262 | + |
| 263 | + // Save a draft. |
| 264 | + require.NoError(t, imapClient.Append("Drafts", []string{imap.DraftFlag}, time.Now(), bytes.NewReader(b))) |
| 265 | + |
| 266 | + // Assert that the draft exists and is marked as a draft. |
| 267 | + { |
| 268 | + messages, err := clientFetch(imapClient, "Drafts") |
| 269 | + require.NoError(t, err) |
| 270 | + require.Len(t, messages, 1) |
| 271 | + require.Contains(t, messages[0].Flags, imap.DraftFlag) |
| 272 | + } |
| 273 | + |
| 274 | + // Connect the SMTP client. |
| 275 | + smtpClient, err := smtp.Dial(net.JoinHostPort(constants.Host, fmt.Sprint(bridge.GetSMTPPort()))) |
| 276 | + require.NoError(t, err) |
| 277 | + defer smtpClient.Close() //nolint:errcheck |
| 278 | + |
| 279 | + // Upgrade to TLS. |
| 280 | + require.NoError(t, smtpClient.StartTLS(&tls.Config{InsecureSkipVerify: true})) |
| 281 | + |
| 282 | + // Authorize with SASL PLAIN. |
| 283 | + require.NoError(t, smtpClient.Auth(sasl.NewPlainClient( |
| 284 | + userInfo.Addresses[0], |
| 285 | + userInfo.Addresses[0], |
| 286 | + string(userInfo.BridgePass)), |
| 287 | + )) |
| 288 | + |
| 289 | + // Send the message. |
| 290 | + require.NoError(t, smtpClient.SendMail( |
| 291 | + userInfo.Addresses[0], |
| 292 | + []string{"recipient@" + s.GetDomain()}, |
| 293 | + bytes.NewReader(b), |
| 294 | + )) |
| 295 | + |
| 296 | + // Delete the draft: add the \Deleted flag and expunge. |
| 297 | + { |
| 298 | + status, err := imapClient.Select("Drafts", false) |
| 299 | + require.NoError(t, err) |
| 300 | + require.Equal(t, uint32(1), status.Messages) |
| 301 | + |
| 302 | + // Add the \Deleted flag. |
| 303 | + require.NoError(t, clientStore(imapClient, 1, 1, true, imap.FormatFlagsOp(imap.AddFlags, true), imap.DeletedFlag)) |
| 304 | + |
| 305 | + // Expunge. |
| 306 | + require.NoError(t, imapClient.Expunge(nil)) |
| 307 | + } |
| 308 | + |
| 309 | + // Assert that the draft is eventually gone. |
| 310 | + require.Eventually(t, func() bool { |
| 311 | + status, err := imapClient.Select("Drafts", false) |
| 312 | + require.NoError(t, err) |
| 313 | + return status.Messages == 0 |
| 314 | + }, 10*time.Second, 100*time.Millisecond) |
| 315 | + |
| 316 | + // Assert that the message is eventually in the sent folder. |
| 317 | + require.Eventually(t, func() bool { |
| 318 | + messages, err := clientFetch(imapClient, "Sent") |
| 319 | + require.NoError(t, err) |
| 320 | + return len(messages) == 1 |
| 321 | + }, 10*time.Second, 100*time.Millisecond) |
| 322 | + |
| 323 | + // Assert that the message is not marked as a draft. |
| 324 | + { |
| 325 | + messages, err := clientFetch(imapClient, "Sent") |
| 326 | + require.NoError(t, err) |
| 327 | + require.Len(t, messages, 1) |
| 328 | + require.NotContains(t, messages[0].Flags, imap.DraftFlag) |
| 329 | + } |
| 330 | + }) |
| 331 | + }) |
| 332 | +} |
0 commit comments