From c1f20ca1b00da81e2c71d1456749250b3cc7c3a5 Mon Sep 17 00:00:00 2001 From: lqleon1214 Date: Fri, 4 Sep 2026 09:52:16 +0800 Subject: [PATCH 1/6] fix: use correct precision format for v2 write API --- src/client.rs | 7 ++++++- src/precision.rs | 25 ++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/client.rs b/src/client.rs index fbb39e1..4162500 100644 --- a/src/client.rs +++ b/src/client.rs @@ -165,7 +165,12 @@ impl Client { (url, p) }; - params.push(("precision", opts.precision.as_str().to_string())); + let precision_str = if opts.use_v2_api { + opts.precision.as_v2_str() + } else { + opts.precision.as_str() + }; + params.push(("precision", precision_str.to_string())); // Compress once; each attempt re-sends the same (Arc-backed) Bytes. let (final_body, compressed) = maybe_gzip(body, opts.gzip_threshold).await?; diff --git a/src/precision.rs b/src/precision.rs index 22e053a..3069478 100644 --- a/src/precision.rs +++ b/src/precision.rs @@ -18,7 +18,7 @@ pub enum Precision { } impl Precision { - /// Returns the API query-parameter string for this precision. + /// Returns the v3 API query-parameter string for this precision. pub fn as_str(self) -> &'static str { match self { Precision::Nanosecond => "nanosecond", @@ -28,6 +28,16 @@ impl Precision { } } + /// Returns the v2 API query-parameter string for this precision. + pub fn as_v2_str(self) -> &'static str { + match self { + Precision::Nanosecond => "ns", + Precision::Microsecond => "us", + Precision::Millisecond => "ms", + Precision::Second => "s", + } + } + /// Number of nanoseconds in one unit of this precision. pub(crate) fn nanos_per_unit(self) -> i64 { match self { @@ -80,4 +90,17 @@ mod tests { assert_eq!(p.scale_timestamp(ns), scaled); } } + + #[test] + fn v2_str_roundtrip() { + for (p, expected) in [ + (Precision::Nanosecond, "ns"), + (Precision::Microsecond, "us"), + (Precision::Millisecond, "ms"), + (Precision::Second, "s"), + ] { + assert_eq!(p.as_v2_str(), expected); + assert_eq!(expected.parse::().unwrap(), p); + } + } } From ce984e5a0d9146bca9ed5f09d36ee2a7593884a0 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Thu, 1 Oct 2026 13:02:28 +0200 Subject: [PATCH 2/6] tests: update v2 write uses bucket query parameter test. --- tests/write_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/write_tests.rs b/tests/write_tests.rs index e1587f4..d7c902d 100644 --- a/tests/write_tests.rs +++ b/tests/write_tests.rs @@ -52,7 +52,7 @@ async fn v2_write_uses_bucket_query_parameter() { .mock("POST", "/api/v2/write") .match_query(Matcher::AllOf(vec![ Matcher::UrlEncoded("bucket".into(), "testdb".into()), - Matcher::UrlEncoded("precision".into(), "nanosecond".into()), + Matcher::UrlEncoded("precision".into(), "ns".into()), ])) .match_header("Authorization", "Bearer test-token") .match_header("Content-Type", Matcher::Regex("text/plain.*".into())) From 68a87d9be02230a30da68bedfb3a37eafccf42c9 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Thu, 1 Oct 2026 14:56:23 +0200 Subject: [PATCH 3/6] tests: adds extra unit and integration tests for v2 precision. --- tests/client.rs | 60 ++++++++++++++++++++++++++++++++++++- tests/write_tests.rs | 70 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index fe3e4cd..4c94c37 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -1,6 +1,6 @@ use std::time::{SystemTime, UNIX_EPOCH}; -use influxdb3_client::{Client, ClientConfig, Error, Point, Row, Value, WriteOptions}; +use influxdb3_client::{Client, ClientConfig, Error, Point, Precision, Row, Value, WriteOptions}; const MEASUREMENT: &str = "rust_e2e"; const LOCATION: &str = "sun-valley-1"; @@ -183,6 +183,64 @@ async fn partial_write_error() -> Result<(), Box> { Ok(()) } +#[tokio::test] +async fn write_and_query_data_with_v2_precision() -> Result<(), Box> { + let Some(config) = testing_config() else { + eprintln!("skipping e2e test: TESTING_INFLUXDB_* env vars are not set"); + return Ok(()); + }; + + let v2_write_options = WriteOptions { + precision: Precision::Second, + default_tags: config.write_options.default_tags.clone(), + gzip_threshold: config.write_options.gzip_threshold, + no_sync: config.write_options.no_sync, + accept_partial: config.write_options.accept_partial, + use_v2_api: true, + tag_order: config.write_options.tag_order, + batch_size: config.write_options.batch_size, + max_inflight: config.write_options.max_inflight, + }; + + let v2_config = ClientConfig::builder() + .host(config.host.clone()) + .database(config.database.clone()) + .token(config.token.unwrap()) + .write_options(v2_write_options) + .build() + .expect("v2 config should be built correctly"); + + let client = Client::new(v2_config).await?; + let test_id = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos() as i64; + + let point = Point::new(MEASUREMENT) + .tag("location", LOCATION) + .field("temp", 15.5_f64) + .field("index", 80_i64) + .field("uindex", 800_u64) + .field("valid", true) + .field("testId", test_id) + .field("text", "a1") + .timestamp_nanos(test_id); + + client.write(vec![point]).await?; + + let row = query_written_point(&client, test_id) + .await? + .unwrap_or_else(|| panic!("expected to query back point with test_id={test_id}")); + + assert_eq!(row["location"].as_str(), Some(LOCATION)); + assert_eq!(row["temp"].as_f64(), Some(15.5)); + assert_eq!(row["index"].as_i64(), Some(80)); + assert_eq!(row["uindex"], Value::U64(800)); + assert_eq!(row["valid"].as_bool(), Some(true)); + assert_eq!(row["testId"].as_i64(), Some(test_id)); + assert_eq!(row["text"].as_str(), Some("a1")); + assert_eq!(row["time"], Value::Timestamp((test_id / 1_000_000_000 ) * 1_000_000_000 )); + + Ok(()) +} + async fn query_written_point( client: &Client, test_id: i64, diff --git a/tests/write_tests.rs b/tests/write_tests.rs index d7c902d..a24381a 100644 --- a/tests/write_tests.rs +++ b/tests/write_tests.rs @@ -1,6 +1,6 @@ use influxdb3_client::error::LineError; /// Write-path integration tests against a mockito HTTP server. -use influxdb3_client::{Client, ClientConfig, Error, Point, Precision}; +use influxdb3_client::{Client, ClientConfig, Error, Point, Precision, WriteOptions}; use mockito::{Matcher, Server}; async fn make_client(server: &Server) -> Client { @@ -586,3 +586,71 @@ async fn test_write_error_classification() { _m.assert_async().await; } } + +#[tokio::test] +async fn test_v2_precision(){ + struct V2PrecisionCase { + url_encode: &'static str, + precision: Precision, + } + + let precision_cases = vec![ + V2PrecisionCase{ + url_encode: "ns", + precision: Precision::Nanosecond, + }, + V2PrecisionCase{ + url_encode: "us", + precision: Precision::Microsecond, + }, + V2PrecisionCase{ + url_encode: "ms", + precision: Precision::Millisecond, + }, + V2PrecisionCase{ + url_encode: "s", + precision: Precision::Second, + }, + ]; + + for case in precision_cases { + let mut server = Server::new_async().await; + + let _m = server + .mock("POST", "/api/v2/write") + .match_query(Matcher::AllOf(vec![ + Matcher::UrlEncoded("precision".into(), case.url_encode.into()), + ])) + .with_status(204) + .expect_at_least(1) + .create_async() + .await; + + let write_options = WriteOptions { + precision: case.precision, + default_tags: Default::default(), + gzip_threshold: None, + no_sync: false, + accept_partial: false, + use_v2_api: true, + tag_order: vec![], + batch_size: 0, + max_inflight: 0, + }; + + let client = Client::new( + ClientConfig::builder() + .host(server.url()) + .database("testdb") + .token("test-token") + .write_options(write_options) + .build() + .unwrap(), + ).await.unwrap(); + + client.write("cpu usage=1.0").await.expect(format!("Write with precision {} as \"{}\" should succeed", + case.precision, case.url_encode).as_str()); + + _m.assert_async().await; + } +} From d597bc05c25a0b3c4e1e4854f08aecaca3950483 Mon Sep 17 00:00:00 2001 From: karel rehor Date: Thu, 1 Oct 2026 15:26:32 +0200 Subject: [PATCH 4/6] chore: fix linter issues in tests --- tests/client.rs | 5 ++++- tests/write_tests.rs | 30 +++++++++++++++++++----------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/tests/client.rs b/tests/client.rs index 4c94c37..8398fdb 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -236,7 +236,10 @@ async fn write_and_query_data_with_v2_precision() -> Result<(), Box Date: Thu, 1 Oct 2026 15:45:30 +0200 Subject: [PATCH 5/6] chore: fix lint issue. --- tests/write_tests.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/write_tests.rs b/tests/write_tests.rs index 5b412ca..fcd76f8 100644 --- a/tests/write_tests.rs +++ b/tests/write_tests.rs @@ -651,13 +651,7 @@ async fn test_v2_precision() { .await .unwrap(); - client.write("cpu usage=1.0").await.expect( - format!( - "Write with precision {} as \"{}\" should succeed", - case.precision, case.url_encode - ) - .as_str(), - ); + client.write("cpu usage=1.0").await; _m.assert_async().await; } From f695bb89095e10c00ecbbd6a0745d57923249a9b Mon Sep 17 00:00:00 2001 From: karel rehor Date: Thu, 1 Oct 2026 16:00:20 +0200 Subject: [PATCH 6/6] chore: fix lint issue --- tests/write_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/write_tests.rs b/tests/write_tests.rs index fcd76f8..49616de 100644 --- a/tests/write_tests.rs +++ b/tests/write_tests.rs @@ -651,7 +651,7 @@ async fn test_v2_precision() { .await .unwrap(); - client.write("cpu usage=1.0").await; + let _ = client.write("cpu usage=1.0").await; _m.assert_async().await; }