fix: Persistence of subscription state for existing contacts
This commit is contained in:
@@ -733,4 +733,304 @@ describe('Actions API Integration Tests', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ========================================
|
||||
// SUBSCRIPTION STATUS PRESERVATION
|
||||
// ========================================
|
||||
describe('Subscription Status Preservation', () => {
|
||||
describe('/v1/send endpoint', () => {
|
||||
it('should NOT change subscription status when sending to subscribed contact without subscribed field', async () => {
|
||||
// Create a subscribed contact
|
||||
const contact = await factories.createContact({
|
||||
projectId,
|
||||
subscribed: true,
|
||||
email: '[email protected]',
|
||||
});
|
||||
|
||||
// Verify initial state
|
||||
expect(contact.subscribed).toBe(true);
|
||||
|
||||
// Send transactional email without specifying subscribed field
|
||||
await EmailService.sendTransactionalEmail({
|
||||
projectId,
|
||||
contactId: contact.id,
|
||||
subject: 'Test',
|
||||
body: 'Test',
|
||||
from: '[email protected]',
|
||||
});
|
||||
|
||||
// Verify subscription status unchanged
|
||||
const updatedContact = await prisma.contact.findUnique({
|
||||
where: {id: contact.id},
|
||||
});
|
||||
|
||||
expect(updatedContact?.subscribed).toBe(true);
|
||||
});
|
||||
|
||||
it('should NOT change subscription status when sending to unsubscribed contact without subscribed field', async () => {
|
||||
// Create an unsubscribed contact
|
||||
const contact = await factories.createContact({
|
||||
projectId,
|
||||
subscribed: false,
|
||||
email: '[email protected]',
|
||||
});
|
||||
|
||||
// Verify initial state
|
||||
expect(contact.subscribed).toBe(false);
|
||||
|
||||
// Send transactional email without specifying subscribed field
|
||||
await EmailService.sendTransactionalEmail({
|
||||
projectId,
|
||||
contactId: contact.id,
|
||||
subject: 'Test',
|
||||
body: 'Test',
|
||||
from: '[email protected]',
|
||||
});
|
||||
|
||||
// Verify subscription status unchanged (should still be false)
|
||||
const updatedContact = await prisma.contact.findUnique({
|
||||
where: {id: contact.id},
|
||||
});
|
||||
|
||||
expect(updatedContact?.subscribed).toBe(false);
|
||||
});
|
||||
|
||||
it('should allow explicit subscription when subscribed=true is provided', async () => {
|
||||
// Create an unsubscribed contact
|
||||
const contact = await factories.createContact({
|
||||
projectId,
|
||||
subscribed: false,
|
||||
email: '[email protected]',
|
||||
});
|
||||
|
||||
// This test would need to be implemented at the controller level
|
||||
// since EmailService.sendTransactionalEmail doesn't accept subscribed parameter
|
||||
// For now, verify the schema allows it
|
||||
const result = ActionSchemas.send.safeParse({
|
||||
to: contact.email,
|
||||
subject: 'Test',
|
||||
body: 'Test',
|
||||
from: '[email protected]',
|
||||
subscribed: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.subscribed).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow explicit unsubscription when subscribed=false is provided', async () => {
|
||||
// Verify the schema allows explicit false
|
||||
const result = ActionSchemas.send.safeParse({
|
||||
to: '[email protected]',
|
||||
subject: 'Test',
|
||||
body: 'Test',
|
||||
from: '[email protected]',
|
||||
subscribed: false,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.subscribed).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it('should default to undefined when subscribed field is omitted', () => {
|
||||
const result = ActionSchemas.send.safeParse({
|
||||
to: '[email protected]',
|
||||
subject: 'Test',
|
||||
body: 'Test',
|
||||
from: '[email protected]',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
// Should be undefined, not false
|
||||
expect(result.data.subscribed).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('/v1/track endpoint', () => {
|
||||
it('should NOT change subscription status when tracking event for subscribed contact', async () => {
|
||||
// Create a subscribed contact
|
||||
const contact = await factories.createContact({
|
||||
projectId,
|
||||
subscribed: true,
|
||||
email: '[email protected]',
|
||||
});
|
||||
|
||||
// Verify initial state
|
||||
expect(contact.subscribed).toBe(true);
|
||||
|
||||
// Track event without specifying subscribed field
|
||||
// This would be done via ContactService.upsert in the track endpoint
|
||||
const {ContactService} = await import('../../services/ContactService.js');
|
||||
await ContactService.upsert(projectId, contact.email, {event: 'test'}, undefined);
|
||||
|
||||
// Verify subscription status unchanged
|
||||
const updatedContact = await prisma.contact.findUnique({
|
||||
where: {id: contact.id},
|
||||
});
|
||||
|
||||
expect(updatedContact?.subscribed).toBe(true);
|
||||
});
|
||||
|
||||
it('should NOT re-subscribe unsubscribed contact when tracking event', async () => {
|
||||
// Create an unsubscribed contact
|
||||
const contact = await factories.createContact({
|
||||
projectId,
|
||||
subscribed: false,
|
||||
email: '[email protected]',
|
||||
});
|
||||
|
||||
// Verify initial state
|
||||
expect(contact.subscribed).toBe(false);
|
||||
|
||||
// Track event without specifying subscribed field
|
||||
const {ContactService} = await import('../../services/ContactService.js');
|
||||
await ContactService.upsert(projectId, contact.email, {event: 'test'}, undefined);
|
||||
|
||||
// Verify subscription status unchanged (should still be false, NOT re-subscribed)
|
||||
const updatedContact = await prisma.contact.findUnique({
|
||||
where: {id: contact.id},
|
||||
});
|
||||
|
||||
expect(updatedContact?.subscribed).toBe(false);
|
||||
});
|
||||
|
||||
it('should create new contacts as subscribed when subscribed is undefined', async () => {
|
||||
const newEmail = '[email protected]';
|
||||
|
||||
// Track event for new contact without specifying subscribed
|
||||
const {ContactService} = await import('../../services/ContactService.js');
|
||||
const contact = await ContactService.upsert(projectId, newEmail, {event: 'test'}, undefined);
|
||||
|
||||
// New contacts should default to subscribed=true
|
||||
expect(contact.subscribed).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow explicit subscription when subscribed=true is provided', async () => {
|
||||
const result = ActionSchemas.track.safeParse({
|
||||
event: 'test',
|
||||
email: '[email protected]',
|
||||
subscribed: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.subscribed).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow explicit unsubscription when subscribed=false is provided', async () => {
|
||||
const result = ActionSchemas.track.safeParse({
|
||||
event: 'test',
|
||||
email: '[email protected]',
|
||||
subscribed: false,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.subscribed).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it('should default to undefined when subscribed field is omitted', () => {
|
||||
const result = ActionSchemas.track.safeParse({
|
||||
event: 'test',
|
||||
email: '[email protected]',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
// Should be undefined, not true
|
||||
expect(result.data.subscribed).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('ContactService.upsert behavior', () => {
|
||||
it('should preserve subscription status when undefined is passed for existing contact', async () => {
|
||||
// Create a subscribed contact
|
||||
const contact = await factories.createContact({
|
||||
projectId,
|
||||
subscribed: true,
|
||||
email: '[email protected]',
|
||||
});
|
||||
|
||||
const {ContactService} = await import('../../services/ContactService.js');
|
||||
|
||||
// Update with undefined subscribed
|
||||
await ContactService.upsert(projectId, contact.email, {firstName: 'John'}, undefined);
|
||||
|
||||
const updated = await prisma.contact.findUnique({
|
||||
where: {id: contact.id},
|
||||
});
|
||||
|
||||
expect(updated?.subscribed).toBe(true);
|
||||
});
|
||||
|
||||
it('should preserve unsubscribed status when undefined is passed', async () => {
|
||||
// Create an unsubscribed contact
|
||||
const contact = await factories.createContact({
|
||||
projectId,
|
||||
subscribed: false,
|
||||
email: '[email protected]',
|
||||
});
|
||||
|
||||
const {ContactService} = await import('../../services/ContactService.js');
|
||||
|
||||
// Update with undefined subscribed
|
||||
await ContactService.upsert(projectId, contact.email, {firstName: 'Jane'}, undefined);
|
||||
|
||||
const updated = await prisma.contact.findUnique({
|
||||
where: {id: contact.id},
|
||||
});
|
||||
|
||||
expect(updated?.subscribed).toBe(false);
|
||||
});
|
||||
|
||||
it('should allow explicit subscription change to true', async () => {
|
||||
// Create an unsubscribed contact
|
||||
const contact = await factories.createContact({
|
||||
projectId,
|
||||
subscribed: false,
|
||||
email: '[email protected]',
|
||||
});
|
||||
|
||||
const {ContactService} = await import('../../services/ContactService.js');
|
||||
|
||||
// Explicitly subscribe
|
||||
await ContactService.upsert(projectId, contact.email, {}, true);
|
||||
|
||||
const updated = await prisma.contact.findUnique({
|
||||
where: {id: contact.id},
|
||||
});
|
||||
|
||||
expect(updated?.subscribed).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow explicit subscription change to false', async () => {
|
||||
// Create a subscribed contact
|
||||
const contact = await factories.createContact({
|
||||
projectId,
|
||||
subscribed: true,
|
||||
email: '[email protected]',
|
||||
});
|
||||
|
||||
const {ContactService} = await import('../../services/ContactService.js');
|
||||
|
||||
// Explicitly unsubscribe
|
||||
await ContactService.upsert(projectId, contact.email, {}, false);
|
||||
|
||||
const updated = await prisma.contact.findUnique({
|
||||
where: {id: contact.id},
|
||||
});
|
||||
|
||||
expect(updated?.subscribed).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user