DatabaseBackendTest.java

  1package eu.siacs.conversations.persistance;
  2
  3import android.content.ContentValues;
  4import android.database.sqlite.SQLiteDatabase;
  5import androidx.test.core.app.ApplicationProvider;
  6import androidx.test.ext.junit.runners.AndroidJUnit4;
  7
  8import org.json.JSONException;
  9import org.json.JSONObject;
 10import org.junit.After;
 11import org.junit.Assert;
 12import org.junit.Before;
 13import org.junit.BeforeClass;
 14import org.junit.Test;
 15import org.junit.runner.RunWith;
 16
 17import java.util.HashMap;
 18import java.util.UUID;
 19
 20import eu.siacs.conversations.entities.Account;
 21import eu.siacs.conversations.entities.Conversation;
 22import eu.siacs.conversations.entities.MucOptions;
 23import eu.siacs.conversations.xml.Namespace;
 24import im.conversations.android.xmpp.model.disco.info.Feature;
 25import im.conversations.android.xmpp.model.disco.info.InfoQuery;
 26
 27@RunWith(AndroidJUnit4.class)
 28public class DatabaseBackendTest {
 29    private record AccountFixture(String uuid, String username, String server) {
 30        void write(DatabaseBackend db) {
 31            final var cv = new ContentValues();
 32            cv.put("uuid", uuid);
 33            cv.put("username", username);
 34            cv.put("server", server);
 35            cv.put("password", "test");
 36            cv.put("options", 0);
 37            db.getWritableDatabase().insertWithOnConflict(
 38                "accounts", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
 39        }
 40    }
 41
 42    private record ConversationFixture(
 43        String conversationUuid,
 44        AccountFixture account,
 45        String name,
 46        String contactJid,
 47        String attributes,
 48        HashMap<MucOptions.User.OccupantId, MucOptions.User.CacheEntry> occupantCache
 49    ) {
 50        void writeConversation(DatabaseBackend db) {
 51            final var cv = new ContentValues();
 52            cv.put("uuid", conversationUuid);
 53            cv.put("name", name);
 54            cv.put("contactUuid", "");
 55            cv.put("accountUuid", account.uuid());
 56            cv.put("contactJid", contactJid);
 57            cv.put("created", System.currentTimeMillis());
 58            cv.put("status", Conversation.STATUS_AVAILABLE);
 59            cv.put("mode", Conversation.MODE_MULTI);
 60            cv.put("attributes", attributes);
 61            db.getWritableDatabase().insert("conversations", null, cv);
 62        }
 63
 64        void writeOccupants(DatabaseBackend db) {
 65            for (final var entry : occupantCache.entrySet()) {
 66                final var cv = new ContentValues();
 67                cv.put(MucOptions.User.CacheEntry.OCCUPANT_ID, entry.getKey().inner());
 68                cv.put(MucOptions.User.CacheEntry.CONVERSATION_UUID, conversationUuid);
 69                cv.put(MucOptions.User.CacheEntry.AVATAR, entry.getValue().avatar());
 70                cv.put(MucOptions.User.CacheEntry.NICK, entry.getValue().nick());
 71                db.getWritableDatabase().insert(
 72                    MucOptions.User.CacheEntry.TABLENAME, null, cv);
 73            }
 74        }
 75
 76        void writeAll(DatabaseBackend db) {
 77            writeConversation(db);
 78            writeOccupants(db);
 79        }
 80
 81        Conversation extractAndConfigure(DatabaseBackend db)
 82        {
 83            final var conversations = db.getConversations(Conversation.STATUS_AVAILABLE);
 84            Assert.assertNotNull("getConversations should not return null", conversations);
 85
 86            Conversation match = null;
 87            for (final var c : conversations) {
 88                if (conversationUuid.equals(c.getUuid())) {
 89                    match = c;
 90                    break;
 91                }
 92            }
 93            Assert.assertNotNull(
 94                "Fixture conversation " + conversationUuid + " not found", match);
 95
 96            match.setAccount(db.getAccounts().get(0));
 97            match.getMucOptions().updateConfiguration(INFO_QUERY_WITH_OCCUPANT_ID);
 98            match.putAllInMucOccupantCache(db.getMucUsersForConversation(match));
 99            return match;
100        }
101    }
102
103    private DatabaseBackend db;
104    private static final InfoQuery INFO_QUERY_WITH_OCCUPANT_ID = new InfoQuery();
105
106    private static AccountFixture ACCOUNT;
107    private static ConversationFixture CONFORMING;
108    private static ConversationFixture NO_CACHED_MUC_USERS;
109    private static ConversationFixture[] FIXTURES;
110
111    @BeforeClass
112    public static void setupClass() throws JSONException {
113        final var occupantIdFeature = new Feature();
114        occupantIdFeature.setVar(Namespace.OCCUPANT_ID);
115        INFO_QUERY_WITH_OCCUPANT_ID.addChild(occupantIdFeature);
116
117        ACCOUNT = new AccountFixture(
118            UUID.randomUUID().toString(), "test", "example.com");
119
120        final var conformingCache =
121            new HashMap<MucOptions.User.OccupantId, MucOptions.User.CacheEntry>();
122        conformingCache.put(
123            new MucOptions.User.OccupantId(UUID.randomUUID().toString()),
124            new MucOptions.User.CacheEntry(UUID.randomUUID().toString(), "ConformingUser"));
125
126        CONFORMING = new ConversationFixture(
127            UUID.randomUUID().toString(),
128            ACCOUNT,
129            "Normal MUC",
130            "normalroom@conference.example.com",
131            new JSONObject().put("mucNick", "testMucNick").toString(),
132            conformingCache
133        );
134
135        NO_CACHED_MUC_USERS = new ConversationFixture(
136            UUID.randomUUID().toString(),
137            ACCOUNT,
138            "Empty Cache MUC",
139            "emptycache@conference.example.com",
140            new JSONObject().put("mucNick", "testMucNick").toString(),
141            new HashMap<>()
142        );
143
144        FIXTURES = new ConversationFixture[] { CONFORMING, NO_CACHED_MUC_USERS };
145    }
146
147    @Before
148    public void setUp() throws Exception {
149        db = DatabaseBackend.getInstance(
150                ApplicationProvider.getApplicationContext());
151        ACCOUNT.write(db);
152        for (final var fixture : FIXTURES) {
153            fixture.writeAll(db);
154        }
155    }
156
157    @After
158    public void tearDown() {
159        SQLiteDatabase sqDb = db.getWritableDatabase();
160        sqDb.delete(Conversation.TABLENAME, null, null);
161        sqDb.delete(Account.TABLENAME, null, null);
162        sqDb.delete(MucOptions.User.CacheEntry.TABLENAME, null, null);
163    }
164
165    @Test
166    public void getConversationsCorrectlyReadsMucUsers() throws Exception {
167        Assert.assertTrue(
168            "Occupant cache should be empty when no occupants are written",
169            NO_CACHED_MUC_USERS
170                .extractAndConfigure(db)
171                    .getMucOccupantCache()
172                    .isEmpty()
173        );
174
175        Assert.assertEquals(
176            "Cached entries should match fixture",
177            CONFORMING
178                .extractAndConfigure(db)
179                .getMucOccupantCache(),
180            CONFORMING.occupantCache()
181        );
182    }
183
184    @Test
185    public void updateConversationWritesMucOccupantsCache() throws Exception {
186        final var conversation = NO_CACHED_MUC_USERS.extractAndConfigure(db);
187        conversation.putAllInMucOccupantCache(CONFORMING.occupantCache());
188        db.updateConversation(conversation);
189
190        final var readBackCache = db.getMucUsersForConversation(conversation);
191        Assert.assertEquals(
192            "Cache should match after updateConversation",
193            CONFORMING.occupantCache(),
194            readBackCache
195        );
196    }
197}