'''
'''
#  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.

Test.Summary = '''
Conditional Slicing test
'''

# Test description:
# Preload the cache with the entire asset to be range requested.
# Reload remap rule with slice plugin
# Request content through the slice plugin

Test.SkipUnless(
    Condition.PluginExists('slice.so'),
    Condition.PluginExists('cache_range_requests.so'),
    Condition.PluginExists('xdebug.so'),
)
Test.ContinueOnFail = False

# configure origin server
server = Test.MakeOriginServer("server", lookup_key="{PATH}{%Range}", options={'-v': None})

# Define ATS and configure
ts = Test.MakeATSProcess("ts")

# small object, should not be sliced
req_small = {
    "headers": "GET /small HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "\r\n",
    "body": "",
}
res_small = {
    "headers": "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Cache-Control: max-age=10,public\r\n" + "\r\n",
    "body": "smol",
}
server.addResponse("sessionlog.json", req_small, res_small)

# large object, all in one slice
req_large = {
    "headers": "GET /large HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "\r\n",
    "body": "",
}
res_large = {
    "headers": "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Cache-Control: max-age=10,public\r\n" + "\r\n",
    "body": "unsliced large object!"
}
server.addResponse("sessionlog.json", req_large, res_large)

# large object, this populates the individual slices in the server

large_body = "large object sliced!"
body_len = len(large_body)
slice_begin = 0
slice_block_size = 10
while (slice_begin < body_len):
    slice_end = slice_begin + slice_block_size
    req_large_slice = {
        "headers": "GET /large HTTP/1.1\r\n" + "Host: www.example.com\r\n" + f"Range: bytes={slice_begin}-{slice_end - 1}" + "\r\n",
        "body": "",
    }
    if slice_end > body_len:
        slice_end = body_len
    res_large_slice = {
        "headers":
            "HTTP/1.1 206 Partial Content\r\n" + "Connection: close\r\n" + "Accept-Ranges: bytes\r\n" +
            f"Content-Range: bytes {slice_begin}-{slice_end - 1}/{body_len}\r\n" + "Cache-Control: max-age=10,public\r\n" + "\r\n",
        "body": large_body[slice_begin:slice_end]
    }
    server.addResponse("sessionlog.json", req_large_slice, res_large_slice)
    slice_begin += slice_block_size

# set up slice plugin with remap host into cache_range_requests
ts.Disk.remap_config.AddLines(
    [
        f'map http://slice/ http://127.0.0.1:{server.Variables.Port}/' +
        f' @plugin=slice.so @pparam=--blockbytes-test={slice_block_size} @pparam=--minimum-size=8 @pparam=--metadata-cache-size=4 @plugin=cache_range_requests.so'
    ])

ts.Disk.plugin_config.AddLine('xdebug.so --enable=x-cache')
ts.Disk.records_config.update(
    {
        'proxy.config.diags.debug.enabled': '0',
        'proxy.config.diags.debug.tags': 'http|cache|slice|xdebug|cache_range_requests',
    })

curl_and_args = '-s -D /dev/stdout -o /dev/stderr -x localhost:{}'.format(ts.Variables.port) + ' -H "x-debug: x-cache"'

# Test case: first request of small object
tr = Test.AddTestRun("Small request 1")
ps = tr.Processes.Default
ps.StartBefore(server, ready=When.PortOpen(server.Variables.Port))
ps.StartBefore(Test.Processes.ts)
tr.MakeCurlCommand(curl_and_args + ' http://slice/small', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr.Content = Testers.ContainsExpression('smol', 'expected smol')
ps.Streams.stdout.Content = Testers.ContainsExpression('X-Cache: miss', 'expected cache miss')
tr.StillRunningAfter = ts

# Test case: second request of small object - expect cache hit
tr = Test.AddTestRun("Small request 2")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/small', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr.Content = Testers.ContainsExpression('smol', 'expected smol')
ps.Streams.stdout.Content = Testers.ContainsExpression('X-Cache: hit-fresh', 'expected cache hit-fresh')
tr.StillRunningAfter = ts

# Test case: range request of small object - expect cache hit (proxy.config.http.cache.range.lookup = 1)
tr = Test.AddTestRun("Small request - ranged")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' -r 1-2 http://slice/small', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr.Content = Testers.ContainsExpression('mo', 'expected mo')
ps.Streams.stdout.Content = Testers.ContainsExpression('X-Cache: hit-fresh', 'expected cache hit-fresh')
tr.StillRunningAfter = ts

# Test case: first request of large object - expect unsliced, cache write disabled
tr = Test.AddTestRun("Large request 1")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/large', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr.Content = Testers.ContainsExpression('unsliced large object!', 'expected large object')
ps.Streams.stdout.Content = Testers.ContainsExpression('X-Cache: miss', 'expected cache miss')
tr.StillRunningAfter = ts

# Test case: first request of large object - expect sliced, cache miss
tr = Test.AddTestRun("Large request 2")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/large', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr.Content = Testers.ContainsExpression('large object sliced!', 'expected large object')
ps.Streams.stdout.Content = Testers.ContainsExpression('X-Cache: miss', 'expected cache miss')
tr.StillRunningAfter = ts

## Test case: first request of large object - expect cache hit
tr = Test.AddTestRun("Large request 3")
ps = tr.Processes.Default
tr.MakeCurlCommand(curl_and_args + ' http://slice/large', ts=ts)
ps.ReturnCode = 0
ps.Streams.stderr.Content = Testers.ContainsExpression('large object sliced!', 'expected large object')
ps.Streams.stdout.Content = Testers.ContainsExpression('X-Cache: hit-fresh', 'expected cache hit-fresh')
tr.StillRunningAfter = ts
