Skip to content

Commit a1b1bb0

Browse files
sdemagnyjbpenrath
authored andcommitted
raw_blob
1 parent 293f772 commit a1b1bb0

File tree

4 files changed

+135
-3
lines changed

4 files changed

+135
-3
lines changed

src/backend/core/factories.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,19 @@ class Meta:
231231
[choice[0] for choice in models.MessageTemplateKindChoices.choices]
232232
)
233233
is_active = True
234+
235+
236+
class BlobFactory(factory.django.DjangoModelFactory):
237+
"""A factory to create blobs for testing purposes."""
238+
239+
class Meta:
240+
model = models.Blob
241+
242+
sha256 = factory.LazyFunction(lambda: b"test_sha256_hash_32_bytes_long")
243+
size = factory.Faker("random_int", min=100, max=10000)
244+
content_type = factory.Faker("mime_type")
245+
compression = factory.fuzzy.FuzzyChoice(
246+
[choice[0] for choice in models.CompressionTypeChoices.choices]
247+
)
248+
raw_content = factory.LazyFunction(lambda: b"test raw content for blob")
249+
mailbox = factory.SubFactory(MailboxFactory)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Generated by Django 5.1.8 on 2025-08-20 06:48
2+
3+
import django.db.models.deletion
4+
import uuid
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('core', '0004_alter_maildomain_custom_attributes_and_more'),
12+
]
13+
14+
operations = [
15+
migrations.CreateModel(
16+
name='MessageTemplate',
17+
fields=[
18+
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
19+
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created on')),
20+
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated on')),
21+
('name', models.CharField(help_text="Name of the template (e.g., 'Standard Reply', 'Out of Office', 'Work Signature')", max_length=255, verbose_name='name')),
22+
('description', models.TextField(blank=True, help_text='Optional description of when to use this template', verbose_name='description')),
23+
('html_body', models.TextField(blank=True, help_text="HTML email body template with placeholders (e.g., '<p>Dear {recipient_name},</p>') or signature content", verbose_name='HTML body')),
24+
('text_body', models.TextField(blank=True, help_text="Plain text email body template with placeholders (e.g., 'Dear {recipient_name},') or signature content", verbose_name='text body')),
25+
('kind', models.SmallIntegerField(choices=[(1, 'reply'), (2, 'new_message'), (3, 'signature')], default=1, help_text='Kind of template (reply, forward, new message, auto reply, signature)', verbose_name='kind')),
26+
('is_active', models.BooleanField(default=True, help_text='Whether this template is available for use', verbose_name='is active')),
27+
],
28+
options={
29+
'verbose_name': 'message template',
30+
'verbose_name_plural': 'message templates',
31+
'db_table': 'messages_messagetemplate',
32+
'ordering': ['name'],
33+
},
34+
),
35+
migrations.AlterField(
36+
model_name='maildomain',
37+
name='custom_attributes',
38+
field=models.JSONField(blank=True, default=dict, help_text='Metadata to sync to the maildomain group in the identity provider.', verbose_name='Custom attributes'),
39+
),
40+
migrations.AlterField(
41+
model_name='user',
42+
name='custom_attributes',
43+
field=models.JSONField(blank=True, default=dict, help_text='Metadata to sync to the user in the identity provider.', verbose_name='Custom attributes'),
44+
),
45+
migrations.CreateModel(
46+
name='MessageTemplateMailbox',
47+
fields=[
48+
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
49+
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created on')),
50+
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated on')),
51+
('is_default', models.BooleanField(default=False, help_text='Whether this template is the default for this mailbox', verbose_name='is default')),
52+
('mailbox', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='template_mailboxes', to='core.mailbox')),
53+
('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='template_mailboxes', to='core.messagetemplate')),
54+
],
55+
options={
56+
'verbose_name': 'message template mailbox',
57+
'verbose_name_plural': 'message template mailboxes',
58+
'db_table': 'messages_messagetemplatemailbox',
59+
'unique_together': {('template', 'mailbox')},
60+
},
61+
),
62+
migrations.AddField(
63+
model_name='messagetemplate',
64+
name='mailboxes',
65+
field=models.ManyToManyField(help_text='Mailboxes that can use this template', related_name='message_templates', through='core.MessageTemplateMailbox', to='core.mailbox'),
66+
),
67+
migrations.CreateModel(
68+
name='MessageTemplateMailDomain',
69+
fields=[
70+
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
71+
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created on')),
72+
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated on')),
73+
('is_default', models.BooleanField(default=False, help_text='Whether this template is the default for this mail domain', verbose_name='is default')),
74+
('maildomain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='template_maildomains', to='core.maildomain')),
75+
('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='template_maildomains', to='core.messagetemplate')),
76+
],
77+
options={
78+
'verbose_name': 'message template mail domain',
79+
'verbose_name_plural': 'message template mail domains',
80+
'db_table': 'messages_messagetemplatemaildomain',
81+
'unique_together': {('template', 'maildomain')},
82+
},
83+
),
84+
migrations.AddField(
85+
model_name='messagetemplate',
86+
name='maildomains',
87+
field=models.ManyToManyField(help_text='Mail domains that can use this template', related_name='message_templates', through='core.MessageTemplateMailDomain', to='core.maildomain'),
88+
),
89+
]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Django 5.1.8 on 2025-08-20 11:21
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('core', '0005_messagetemplate_alter_maildomain_custom_attributes_and_more'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='messagetemplate',
16+
name='raw_blob',
17+
field=models.ForeignKey(blank=True, help_text='Reference to the blob containing raw template data', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='message_templates', to='core.blob'),
18+
),
19+
]

src/backend/core/tests/api/test_message_template.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ def fixture_mailbox():
4040
return factories.MailboxFactory()
4141

4242

43+
@pytest.fixture(name="blob")
44+
def fixture_blob(mailbox):
45+
"""Create a test blob."""
46+
return factories.BlobFactory(mailbox=mailbox)
47+
48+
4349
class TestMessageTemplateViewSet:
4450
"""Test the MessageTemplateViewSet."""
4551

@@ -608,7 +614,7 @@ def test_retrieve_no_access(self, user):
608614
)
609615
assert response.status_code == status.HTTP_404_NOT_FOUND
610616

611-
def test_create(self, user, mailbox):
617+
def test_create(self, user, mailbox, blob):
612618
"""Test creating a new email template."""
613619
factories.MailboxAccessFactory(
614620
mailbox=mailbox,
@@ -655,7 +661,7 @@ def test_create_unauthorized(self):
655661
response = client.post(reverse("message-templates-list"), data)
656662
assert response.status_code == status.HTTP_401_UNAUTHORIZED
657663

658-
def test_update(self, user, mailbox):
664+
def test_update(self, user, mailbox, blob):
659665
"""Test updating an email template."""
660666
factories.MailboxAccessFactory(
661667
mailbox=mailbox,
@@ -700,7 +706,7 @@ def test_update(self, user, mailbox):
700706
template.refresh_from_db()
701707
assert template.raw_blob.get_content().decode("utf-8") == "updated raw content"
702708

703-
def test_partial_update(self, user, mailbox):
709+
def test_partial_update(self, user, mailbox, blob):
704710
"""Test partially updating an email template."""
705711
factories.MailboxAccessFactory(
706712
mailbox=mailbox,
@@ -1055,6 +1061,7 @@ def test_template_abilities_in_response(self, user, mailbox):
10551061
)
10561062
assert response.status_code == status.HTTP_200_OK
10571063
assert "abilities" in response.data
1064+
assert "raw_blob" in response.data # raw_blob field should be present
10581065

10591066
def test_template_list_with_abilities(self, user, mailbox):
10601067
"""Test that template list includes abilities."""
@@ -1081,6 +1088,7 @@ def test_template_list_with_abilities(self, user, mailbox):
10811088
assert response.status_code == status.HTTP_200_OK
10821089
assert len(response.data) == 1
10831090
assert "abilities" in response.data[0]
1091+
assert "raw_blob" in response.data[0] # raw_blob field should be present
10841092

10851093
def test_template_no_access_abilities(self, user):
10861094
"""Test that users without access get no abilities."""

0 commit comments

Comments
 (0)