blob: 8974382dd11fb4447547ce64da4134fd5afa9e14 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use anyhow::Result;
use crate::*;
pub fn tests(op: &Operator, tests: &mut Vec<Trial>) {
let cap = op.info().capability();
if cap.read && cap.write && cap.compose {
tests.extend(async_trials!(
op,
test_compose_ordered_sources,
test_compose_incrementally,
test_compose_empty,
test_compose_self,
test_compose_more_than_provider_request_limit
));
}
if cap.read && cap.write && cap.stat && cap.compose && cap.compose_with_content_type {
tests.extend(async_trials!(op, test_compose_with_content_type));
}
if cap.read && cap.write && cap.stat && cap.compose && cap.compose_with_source_version {
tests.extend(async_trials!(op, test_compose_with_source_version));
}
if cap.read
&& cap.write
&& cap.stat
&& cap.compose
&& (cap.compose_with_source_version || cap.compose_with_source_if_match)
{
tests.extend(async_trials!(op, test_compose_with_source_if_not_changed));
}
if cap.read && cap.write && cap.stat && cap.compose && cap.compose_with_if_version_match {
tests.extend(async_trials!(op, test_compose_with_if_version_match));
}
if cap.read
&& cap.write
&& cap.stat
&& cap.compose
&& (cap.compose_with_if_version_match || cap.compose_with_if_match)
{
tests.extend(async_trials!(op, test_compose_with_if_not_changed));
}
if cap.read && cap.write && cap.compose && cap.compose_with_if_not_exists {
tests.extend(async_trials!(op, test_compose_with_if_not_exists));
}
}
pub async fn test_compose_ordered_sources(op: Operator) -> Result<()> {
let first = TEST_FIXTURE.new_file_path();
let second = TEST_FIXTURE.new_file_path();
let target = TEST_FIXTURE.new_file_path();
op.write(&first, "hello").await?;
op.write(&second, " world").await?;
op.write(&target, "stale").await?;
op.compose([first.as_str(), second.as_str(), first.as_str()], &target)
.await?;
assert_eq!(
op.read(&target).await?.to_bytes().as_ref(),
b"hello worldhello"
);
assert_eq!(op.read(&first).await?.to_bytes().as_ref(), b"hello");
assert_eq!(op.read(&second).await?.to_bytes().as_ref(), b" world");
Ok(())
}
pub async fn test_compose_incrementally(op: Operator) -> Result<()> {
let first = TEST_FIXTURE.new_file_path();
let second = TEST_FIXTURE.new_file_path();
let target = TEST_FIXTURE.new_file_path();
op.write(&first, "a").await?;
op.write(&second, "b").await?;
let mut composer = op.composer_with(&target).concurrent(2).await?;
composer.compose(first.as_str()).await?;
composer
.compose_options(second.as_str(), options::ComposeSourceOptions::default())
.await?;
let metadata = composer.close().await?;
assert_eq!(metadata.content_length(), 2);
assert_eq!(op.read(&target).await?.to_bytes().as_ref(), b"ab");
assert_eq!(composer.close().await?.content_length(), 2);
Ok(())
}
pub async fn test_compose_empty(op: Operator) -> Result<()> {
let target = TEST_FIXTURE.new_file_path();
let err = op
.compose(Vec::<String>::new(), &target)
.await
.expect_err("empty composition must fail");
assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
if op.info().capability().stat {
assert!(!op.exists(&target).await?);
}
Ok(())
}
pub async fn test_compose_self(op: Operator) -> Result<()> {
let path = TEST_FIXTURE.new_file_path();
op.write(&path, "content").await?;
let err = op
.compose([path.as_str()], &path)
.await
.expect_err("composing a destination from itself must fail");
assert_eq!(err.kind(), ErrorKind::IsSameFile);
assert_eq!(op.read(&path).await?.to_bytes().as_ref(), b"content");
Ok(())
}
pub async fn test_compose_more_than_provider_request_limit(op: Operator) -> Result<()> {
let target = TEST_FIXTURE.new_file_path();
let mut sources = Vec::with_capacity(65);
for index in 0..65_u8 {
let path = TEST_FIXTURE.new_file_path();
op.write(&path, vec![index]).await?;
sources.push(path);
}
op.compose_with(sources.iter().map(String::as_str), &target)
.concurrent(4)
.await?;
assert_eq!(
op.read(&target).await?.to_bytes(),
(0..65_u8).collect::<Vec<_>>()
);
Ok(())
}
pub async fn test_compose_with_content_type(op: Operator) -> Result<()> {
let source = TEST_FIXTURE.new_file_path();
let target = TEST_FIXTURE.new_file_path();
op.write(&source, "content").await?;
op.compose_with([source.as_str()], &target)
.content_type("text/plain")
.await?;
assert_eq!(op.stat(&target).await?.content_type(), Some("text/plain"));
Ok(())
}
pub async fn test_compose_with_source_version(op: Operator) -> Result<()> {
let source = TEST_FIXTURE.new_file_path();
let target = TEST_FIXTURE.new_file_path();
op.write(&source, "content").await?;
let metadata = op.stat(&source).await?;
let version = metadata
.version()
.expect("source version capability requires stat to return a version");
let mut composer = op.composer(&target).await?;
composer
.compose_with(source.as_str())
.version(version)
.await?;
composer.close().await?;
assert_eq!(op.read(&target).await?.to_bytes().as_ref(), b"content");
Ok(())
}
pub async fn test_compose_with_source_if_not_changed(op: Operator) -> Result<()> {
let source = TEST_FIXTURE.new_file_path();
let other = TEST_FIXTURE.new_file_path();
let target = TEST_FIXTURE.new_file_path();
let rejected_target = TEST_FIXTURE.new_file_path();
op.write(&source, "source").await?;
let expected = op.stat(&source).await?;
op.write(&other, "other").await?;
op.write(&other, "unrelated").await?;
let unrelated = op.stat(&other).await?;
op.compose(
[(
source.as_str(),
options::ComposeSourceOptions {
if_not_changed: Some(expected),
..Default::default()
},
)],
&target,
)
.await?;
assert_eq!(op.read(&target).await?.to_bytes().as_ref(), b"source");
assert_eq!(op.read(&source).await?.to_bytes().as_ref(), b"source");
op.compose(
[(
source.as_str(),
options::ComposeSourceOptions {
if_not_changed: Some(unrelated),
..Default::default()
},
)],
&rejected_target,
)
.await
.expect_err("an unrelated source identity must not be ignored");
assert!(!op.exists(&rejected_target).await?);
assert_eq!(op.read(&source).await?.to_bytes().as_ref(), b"source");
Ok(())
}
pub async fn test_compose_with_if_version_match(op: Operator) -> Result<()> {
let source = TEST_FIXTURE.new_file_path();
let target = TEST_FIXTURE.new_file_path();
op.write(&source, "new").await?;
op.write(&target, "old").await?;
let metadata = op.stat(&target).await?;
let version = metadata
.version()
.expect("version match capability requires stat to return a version");
op.compose_with([source.as_str()], &target)
.if_version_match(version)
.await?;
assert_eq!(op.read(&target).await?.to_bytes().as_ref(), b"new");
Ok(())
}
pub async fn test_compose_with_if_not_changed(op: Operator) -> Result<()> {
let source = TEST_FIXTURE.new_file_path();
let target = TEST_FIXTURE.new_file_path();
op.write(&source, "composed").await?;
op.write(&target, "initial").await?;
let expected = op.stat(&target).await?;
op.compose_with([source.as_str()], &target)
.if_not_changed(&expected)
.await?;
assert_eq!(op.read(&target).await?.to_bytes().as_ref(), b"composed");
let stale = op.stat(&target).await?;
op.write(&target, "replacement").await?;
let err = op
.compose_with([source.as_str()], &target)
.if_not_changed(&stale)
.await
.expect_err("stale destination metadata must fail");
assert_eq!(err.kind(), ErrorKind::ConditionNotMatch);
assert_eq!(op.read(&target).await?.to_bytes().as_ref(), b"replacement");
Ok(())
}
pub async fn test_compose_with_if_not_exists(op: Operator) -> Result<()> {
let source = TEST_FIXTURE.new_file_path();
let target = TEST_FIXTURE.new_file_path();
op.write(&source, "new").await?;
op.write(&target, "existing").await?;
let err = op
.compose_with([source.as_str()], &target)
.if_not_exists(true)
.await
.expect_err("composition must not replace an existing destination");
assert_eq!(err.kind(), ErrorKind::ConditionNotMatch);
assert_eq!(op.read(&target).await?.to_bytes().as_ref(), b"existing");
Ok(())
}