fix: Persistence of subscription state for existing contacts

This commit is contained in:
Dries Augustyns
2025-12-18 19:43:59 +01:00
parent 23b4ec992d
commit 007a908e83
5 changed files with 589 additions and 6 deletions
@@ -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);
});
});
});
});