Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
* The `MetricsHandler` interface now requires two additional methods: `record_database_load()` and `record_database_migration()`
* In Kotlin expose `GleanMetrics.Pings.nimbusTargetingContext` as `Nimbus.Pings.nimbusTargetingContext` for downstream tests. ([#14542](https://github.com/mozilla/experimenter/issues/14542))

### Remote-Settings
* Removed old remote-settings client code that is no longer used.
* Renaming `RemoteSettingsConfig2` to `RemoteSettingsConfig`, which will require client updates.

[Full Changelog](In progress)

# v149.0 (_2026-02-23_)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ package org.mozilla.experiments.nimbus
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import kotlinx.coroutines.Job
import mozilla.appservices.remotesettings.RemoteSettingsConfig
import mozilla.appservices.remotesettings.RemoteSettingsConfig2
import mozilla.appservices.remotesettings.RemoteSettingsService
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
Expand Down Expand Up @@ -35,7 +35,7 @@ class NimbusBuilderTest {
}.build(
appInfo,
NimbusServerSettings(
rsService = RemoteSettingsService(storageDir = "dummy", config = RemoteSettingsConfig()),
rsService = RemoteSettingsService(storageDir = "dummy", config = RemoteSettingsConfig2()),
collectionName = "nimbus-preview",
),
) as DummyNimbus
Expand Down
7 changes: 3 additions & 4 deletions components/nimbus/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![cfg(feature = "rkv-safe-mode")]

use remote_settings::{RemoteSettingsConfig, RemoteSettingsContext, RemoteSettingsService};
use rkv::StoreOptions;

// utilities shared between tests

use nimbus::error::{Result, debug};
Expand All @@ -16,6 +13,8 @@ use nimbus::metrics::{
use nimbus::stateful::client::NimbusServerSettings;
use nimbus::stateful::persistence::{Database, SingleStore};
use nimbus::{AppContext, NimbusClient, RemoteSettingsServer};
use remote_settings::{RemoteSettingsConfig2, RemoteSettingsContext, RemoteSettingsService};
use rkv::StoreOptions;
use std::{path::Path, sync::Arc};

pub struct NoopMetricsHandler;
Expand Down Expand Up @@ -94,7 +93,7 @@ fn new_test_client_internal(
..Default::default()
};

let config = RemoteSettingsConfig {
let config = RemoteSettingsConfig2 {
server: Some(RemoteSettingsServer::Custom {
url: url.as_str().to_string(),
}),
Expand Down
5 changes: 3 additions & 2 deletions components/nimbus/tests/test_fs_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ mod common;

use std::path::PathBuf;
use std::sync::Arc;

use url::Url;

use nimbus::error::Result;
use nimbus::stateful::client::NimbusServerSettings;
use nimbus::{NimbusClient, RemoteSettingsServer};
use remote_settings::{RemoteSettingsConfig, RemoteSettingsContext, RemoteSettingsService};
use remote_settings::{RemoteSettingsConfig2, RemoteSettingsContext, RemoteSettingsService};

use crate::common::NoopMetricsHandler;

Expand All @@ -29,7 +30,7 @@ fn test_simple() -> Result<()> {

let url = Url::from_file_path(dir).expect("experiments dir should exist");

let config = RemoteSettingsConfig {
let config = RemoteSettingsConfig2 {
server: Some(RemoteSettingsServer::Custom {
url: url.as_str().to_string(),
}),
Expand Down
4 changes: 2 additions & 2 deletions components/relay/src/rs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,11 @@ impl RelayRemoteSettingsClient {
#[cfg(test)]
mod tests {
use super::*;
use remote_settings::{RemoteSettingsConfig, RemoteSettingsServer};
use remote_settings::{RemoteSettingsConfig2, RemoteSettingsServer};

// Helper to create a RemoteSettingsService for testing
fn create_test_remote_settings_service() -> Arc<RemoteSettingsService> {
let config = RemoteSettingsConfig {
let config = RemoteSettingsConfig2 {
server: Some(RemoteSettingsServer::Custom {
url: "http://localhost".to_string(),
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.appservices.remotesettings

import mozilla.appservices.httpconfig.RustHttpConfig
import mozilla.appservices.remotesettings.RemoteSettings
import mozilla.appservices.remotesettings.RemoteSettingsConfig
import mozilla.components.concept.fetch.Client
import mozilla.components.concept.fetch.Headers
import mozilla.components.concept.fetch.MutableHeaders
import mozilla.components.concept.fetch.Request
import mozilla.components.concept.fetch.Response
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import java.io.File
import java.io.InputStream

@RunWith(RobolectricTestRunner::class)
class RemoteSettingsTest {

@Rule @JvmField
val tempDir = TemporaryFolder()

private var fakeUrl = ""
private var fakeStatus = 200
private var fakeHeaders: Headers = MutableHeaders("etag" to "\"1000\"")
private var fakeBodyStream = "".byteInputStream()
private var fakeContentType = ""
private var fakeBody = Response.Body(fakeBodyStream, fakeContentType)
private var doFetch: (Request) -> Response = {
Response(fakeUrl, fakeStatus, fakeHeaders, fakeBody)
}

@Before
fun setup() {
RustHttpConfig.setClient(
lazyOf(object : Client() {
override fun fetch(request: Request): Response = doFetch(request)
}),
)
}

@Test
fun `test setting up, fetching records, and attachment downloading`() {
val config = RemoteSettingsConfig(
serverUrl = "http://localhost:8888",
bucketName = "the-bucket",
collectionName = "the-collection",
)

// Setup the client
val client = RemoteSettings(config)

// Fetch the records
setupRecordResponse(config, responseBodyStream = recordJson.byteInputStream())
val response = client.getRecords()
val records = response.records
assertEquals(records[0].fields.getString("title"), recordTitle)

// Download an attachment
val attachmentLocation = records[0].attachment!!.location
val localAttachmentPath = "${tempDir.root}/path.jpg"
setupAttachmentResponses(config, attachmentLocation)
client.downloadAttachmentToPath(attachmentLocation, localAttachmentPath)
val downloadedFile = File(localAttachmentPath)
assertTrue(downloadedFile.exists())
assertEquals(csv, downloadedFile.readText())
}

private fun setupAttachmentResponses(
config: RemoteSettingsConfig,
attachmentLocation: String,
) {
val topUrl = config.serverUrl
val attachmentUrl = "${config.serverUrl}/attachments/$attachmentLocation"
doFetch = { req ->
when (req.url) {
"$topUrl/v1/" -> {
Response(
url = req.url,
status = 200,
headers = fakeHeaders,
body = Response.Body(
stream = attachmentsInfoJson(topUrl!!).byteInputStream(),
null,
),
)
}
attachmentUrl -> {
Response(
url = attachmentUrl,
status = 200,
headers = fakeHeaders,
body = Response.Body(
stream = csv.byteInputStream(),
null,
),
)
}
else -> error("unexpected url")
}
}
}
private fun setupRecordResponse(
config: RemoteSettingsConfig,
responseBodyStream: InputStream = recordJson.byteInputStream(),
) {
fakeUrl = "${config.serverUrl}/v1/buckets/${config.bucketName}/collections/${config.collectionName}/records"
fakeBody = Response.Body(responseBodyStream, null)
}

private fun attachmentsInfoJson(baseUrl: String) = """
{
"capabilities": {
"admin": {
"description": "Serves the admin console.",
"url": "https://github.com/Kinto/kinto-admin/",
"version": "2.0.0"
},
"attachments": {
"description": "Add file attachments to records",
"url": "https://github.com/Kinto/kinto-attachment/",
"version": "6.3.1",
"base_url": "$baseUrl/attachments/"
}
}
}
""".trimIndent()

private val recordTitle = "with-txt-attachment"
private val recordJson = """
{
"data": [
{
"title": "$recordTitle",
"content": "content",
"attachment": {
"filename": "text-attachment.csv",
"location": "the-bucket/the-collection/d3a5eccc-f0ca-42c3-b0bb-c0d4408c21c9.jpg",
"hash": "2cbd593f3fd5f1585f92265433a6696a863bc98726f03e7222135ff0d8e83543",
"mimetype": "text/csv",
"size": 1374325
},
"schema": 1677694447771,
"id": "7403c6f9-79be-4e0c-a37a-8f2b5bd7ad58",
"last_modified": 1677694455368
}
]
}
""".trimIndent()

private val csv = """
John,Doe,120 jefferson st.,Riverside, NJ, 08075
Jack,McGinnis,220 hobo Av.,Phila, PA,09119
"John ""Da Man""${'"'},Repici,120 Jefferson St.,Riverside, NJ,08075
Stephen,Tyler,"7452 Terrace ""At the Plaza"" road",SomeTown,SD, 91234
,Blankman,,SomeTown, SD, 00298
"Joan ""the bone"", Anne",Jet,"9th, at Terrace plc",Desert City,CO,00123
""".trimIndent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Robolectric doesn't support SDK 36 yet, so pin to 35
sdk=35
Loading